JavaScript addEventListener ('load', function) не ждет, предложения? - PullRequest
0 голосов
/ 10 июня 2019

Обходной путь:

Если это кому-нибудь поможет в будущем, я выкладываю здесь обходной путь.

Самое простое решение также кажется наилучшим: в конце сценария добавленослушатель событий.Это запускает процедуру ImagesLoaded.Который затем запускает тайм-аут.Таким образом, если соединение медленное, тайм-аут не начинается до тех пор, пока не будут загружены все изображения.

(
function ImagesLoaded(w, undefined){
    //place ImagesLoaded.js code here//;}
function timeoutFunction(callfunction, msDelay){
    setTimeout(callfunction, msDelay);}
function ReplaceElements(){
    //code below//;}
document.addEventListener('load', ImagesLoaded(document).always(timeoutFunction(ReplaceElements, 2000)));}(window));
```

Исходная проблема:

Недавно опубликовал тему, получил много хорошегоОбратная связь.Это более чистая версия этого потока.

Проблема: addEventListener ('load') не заставляет скрипт ждать загрузки окна до запуска.

Я запускаю Tampermonkey, aРасширение браузера, которое использует JavaScript, чтобы изменить вещи на веб-сайте в соответствии с вашим предпочтением.На сайте ScienceDirect.com есть одна (не более одной) ссылка DOI.Я создал скрипт, который ищет эту ссылку DOI и заменяет ее другой ссылкой.Последняя строка кода;"window.addEventListener ('load', replaceElement);"должен запускать только функцию «replaceElement» ПОСЛЕ загрузки.

Элемент успешно заменен, и правильный (новый) innerHTML отображается в течение доли секунды.Затем innerHTML возвращается к исходному innerHTML.По какой-то причине сценарий не ожидает загрузки всей страницы, несмотря на функцию addEventListener ('load').

Поскольку на странице всегда есть только одна ссылка DOI, и онавсегда помещенный в объект 'div' с именем 'doi-link', я использую его, чтобы найти элемент 'a', и цикл заканчивается после первого совпадения.Опять же, насколько я могу судить, проблема в преждевременном срабатывании функции replaceElement.Я пытался работать с другим форматированием, перемещая «addEventListener» в начало документа, кажется, ничего не работает.В прошлый раз у меня была похожая проблема, это было из-за форматирования.Я написал это как ('load', ());когда это должно было быть ('load',);Пробовал обе версии, без разницы.

Пробовал перемещать «addEventListener», чтобы он был внутри функции, без разницы.

Кто-то предположил, что страница была запущена для перезагрузки, возможно, потому что она была отправленакак форма.Поэтому я добавил «вернуть ложь», без разницы.Теперь я просто ломаю голову, хотел бы услышать ваши идеи или предложения.

Код ниже - минимум, чтобы воспроизвести проблему.Существует только один element.id, поскольку ссылка на одну страницу встречается только один раз.После нахождения и замены ссылки значение [f] увеличивается, чтобы завершить цикл for.

Элемент успешно заменен.В течение доли секунды отображается правильный (новый) innerHTML.Тогда это возвращается.Похоже, addEventListener не ждет.Независимо от того, как я пытаюсь, innerHTML моей новой ссылки 'a' возвращается обратно к innerHTML элемента 'a', который был там раньше.

// ==UserScript==
// @name         ScienceDriect DOI Button
// @description  Add sci-hub button on article page. Add sci-hub button after article link. Support Google scholar, bing academic and baidu xueshu. Jump CNKI English article to Chinese article.
// @include      https://sciencedirect.com/*
// @include      http://sciencedirect.com/*
// @include      https://www.sciencedirect.com/*
// @include      http://www.sciencedirect.com/*
// @require      http://code.jquery.com/jquery-3.4.1.min.js
// @require      https://unpkg.com/imagesloaded@4.1.4/imagesloaded.pkgd.js
// @require      http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js
// @require      http://cdnjs.cloudflare.com/ajax/libs/sugar/1.3/sugar.min.js
// @grant        GM_setClipboard
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// ==/UserScript==

(
function(w, undefined) {
'use strict';

var ILID = ('il' + Math.random()).replace(/0\./g, '');
var EVENTS = 'load error';
var ALLOWED_NODE_TYPES = [
    1, // ELEMENT_NODE
    9, // DOCUMENT_NODE
    11 // DOCUMENT_FRAGMENT_NODE
];

/**
 * Return type of the value.
 *
 * @param  {Mixed} value
 *
 * @return {String}
 */
function type(value) {
    if (value == null) {
        return String(value);
    }
    if (typeof value === 'object' || typeof value === 'function') {
        return (value instanceof w.NodeList && 'nodelist') ||
            (value instanceof w.HTMLCollection && 'htmlcollection') ||
            Object.prototype.toString.call(value).match(/\s([a-z]+)/i)[1].toLowerCase();
    }
    return typeof value;
}

/**
 * Convert array-like objects into an array.
 *
 * @param  {Mixed} collection
 *
 * @return {Array}
 */
function toArray(collection) {
    switch (type(collection)) {
        case 'array':
            return collection;
        case 'undefined':
            return [];
        case 'nodelist':
        case 'htmlcollection':
        case 'arguments':
            var arr = [];
            for (var i = 0, l = collection.length; i < l; i++) {
                if (i in collection) {
                    arr.push(collection[i]);
                }
            }
            return arr;
        default:
            return [collection];
    }
}

/**
 * Check whether the value is in an array.
 *
 * @param  {Mixed} value
 * @param  {Array} array
 *
 * @return {Boolean}
 */
function inArray(value, array) {
    if (type(array) !== 'array') {
        return -1;
    }
    if (array.indexOf) {
        return array.indexOf(value);
    }
    for (var i=0, l = array.length; i < l; i++) {
        if (array[i] === value) {
            return i;
        }
    }
    return -1;
}

/**
 * Callback proxy.
 *
 * Ensures that callback will receive a specific context.
 *
 * @param {Mixed}    context
 * @param {Function} callback
 */
function proxy(context, callback) {
    return function () {
        return callback.apply(context, arguments);
    };
}

/**
 * Add event listeners to element.
 *
 * @param  {Node}     element
 * @param  {Event}    eventName
 * @param  {Function} handler
 *
 * @return {Void}
 */
function bind(element, eventName, handler) {
    listener(element, eventName, handler);
}

/**
 * Remove event listeners from element.
 *
 * @param  {Node}     element
 * @param  {Event}    eventName
 * @param  {Function} handler
 *
 * @return {Void}
 */
function unbind(element, eventName, handler) {
    listener(element, eventName, handler, 1);
}

/**
 * Manage element event listeners.
 *
 * @param  {Node}     element
 * @param  {Event}    eventName
 * @param  {Function} handler
 * @param  {Bool}     remove
 *
 * @return {Void}
 */
function listener(element, eventName, handler, remove) {
    var events = eventName.split(' ');
    for (var i = 0, l = events.length; i < l; i++) {
        if (element.addEventListener) {
            element[remove ? 'removeEventListener' : 'addEventListener'](events[i], handler, false);
        } else if (element.attachEvent) {
            element[remove ? 'detachEvent' : 'attachEvent']('on' + events[i], handler);
        }
    }
}

/**
 * Callbacks handler.
 */
function Callbacks() {
    var self = this;
    var callbacks = {};
    var i, l;

    /**
     * Registers callbacks.
     *
     * @param  {Mixed} name
     * @param  {Mixed} fn
     *
     * @return {Void}
     */
    self.on = function (name, fn) {
        callbacks[name] = callbacks[name] || [];
        if (type(fn) === 'function' && inArray(fn, callbacks[name]) === -1) {
            callbacks[name].push(fn);
        }
    };

    /**
     * Remove one or all callbacks.
     *
     * @param  {String} name
     * @param  {Mixed}  fn
     *
     * @return {Void}
     */
    self.off = function (name, fn) {
        callbacks[name] = callbacks[name] || [];
        if (fn === undefined) {
            callbacks[name].length = 0;
        } else {
            var index = inArray(fn, callbacks[name]);
            if (index !== -1) {
                callbacks[name].splice(index, 1);
            }
        }
    };

    /**
     * Trigger callbacks for event.
     *
     * @param  {String} name
     * @param  {Mixed}  context
     * @param  {Mixed}  argN
     *
     * @return {Void}
     */
    self.trigger = function (name, context) {
        if (callbacks[name]) {
            for (i = 0, l = callbacks[name].length; i < l; i++) {
                callbacks[name][i].apply(context, Array.prototype.slice.call(arguments, 2));
            }
        }
    };
}

/**
 * Executes callback(s) when images have finished with loading.
 *
 * @param  {NodeList} collection Collection of containers, images, or both.
 * @param  {Function} options    ImagesLoaded options.
 *
 * @return {Void}
 */
function ImagesLoaded(collection, options) {
    // Fill unassigned options with defaults
    options = options || {};
    for (var key in ImagesLoaded.defaults) {
        if (!options.hasOwnProperty(key)) {
            options[key] = ImagesLoaded.defaults[key];
        }
    }

    // Private variables
    var self = this instanceof ImagesLoaded ? this : {};
    var callbacks = new Callbacks();
    var tIndex;

    // Element holders
    self.images = [];
    self.loaded = [];
    self.pending = [];
    self.proper = [];
    self.broken = [];

    // States
    self.isPending = true;
    self.isDone = false;
    self.isFailed = false;

    // Extract images
    collection = toArray(collection);
    for (var c = 0, cl = collection.length; c < cl; c++) {
        if (collection[c].nodeName === 'IMG') {
            self.images.push(collection[c]);
        } else if (inArray(collection[c].nodeType, ALLOWED_NODE_TYPES) !== -1) {
            self.images = self.images.concat(toArray(collection[c].getElementsByTagName('img')));
        }
    }

    /**
     * Registers or executes callback for done state.
     *
     * @param  {Function} callback
     *
     * @return {ImagesLoaded}
     */
    self.done = function (callback) {
        if (self.isPending) {
            callbacks.on('done', callback);
        } else if (self.isDone && type(callback) === 'function') {
            callback.call(self);
        }
        return self;
    };

    /**
     * Registers or executes callback for fail state.
     *
     * @param  {Function} callback
     *
     * @return {ImagesLoaded}
     */
    self.fail = function (callback) {
        if (self.isPending) {
            callbacks.on('fail', callback);
        } else if (self.isFailed && type(callback) === 'function') {
            callback.call(self);
        }
        return self;
    };

    /**
     * Registers or executes callback for done state.
     *
     * @param  {Function} callback
     *
     * @return {ImagesLoaded}
     */
    self.always = function (callback) {
        if (self.isPending) {
            callbacks.on('always', callback);
        } else if (type(callback) === 'function') {
            callback.call(self);
        }
        return self;
    };

    /**
     * Registers or executes callback for done state.
     *
     * @param  {Function} callback
     *
     * @return {ImagesLoaded}
     */
    self.progress = function (callback) {
        if (self.isPending) {
            callbacks.on('progress', callback);
        }
        // Retroactivity
        for (var i = 0, l = self.loaded.length; i < l; i++) {
            callback.call(self, self.loaded[i], self.loaded[i][ILID].isBroken);
        }
        return self;
    };

    /**
     * Executes proper callbacks when all images has finished with loading.
     *
     * @return {Void}
     */
    function doneLoading() {
        if (!self.isPending) {
            return;
        }
        // Clear timeout
        clearTimeout(tIndex);
        // Mark states
        self.isPending = false;
        self.isDone = self.images.length === self.proper.length;
        self.isFailed = !self.isDone;
        // Trigger callbacks
        callbacks.trigger(self.isDone ? 'done' : 'fail', self);
        callbacks.trigger('always', self);
    }

    /**
     * Terminates the determination process prematurely.
     *
     * @return {Void}
     */
    function terminate() {
        // Mark still pending images as broken
        while (self.pending.length) {
            imgLoaded(self.pending[0], 1);
        }
    }

    /**
     * Image load event handler.
     *
     * @param  {Event} event
     *
     * @return {Void}
     */
    function imgLoadedHandler(event) {
        /*jshint validthis:true */
        event = event || w.event;
        // Unbind loaded handler from temporary image
        unbind(this[ILID].tmpImg || {}, EVENTS, imgLoadedHandler);
        // Leave the temporary image for garbage collection
        this[ILID].tmpImg = null;
        // Don't proceed if image is already loaded
        if (inArray(this, self.loaded) === -1) {
            imgLoaded(this, event.type !== 'load');
        }
    }

    /**
     * Mark image as loaded.
     *
     * @param  {Node}    img      Image element.
     * @param  {Boolean} isBroken Whether the image is broken.
     *
     * @return {Void}
     */
    function imgLoaded(img, isBroken) {
        var pendingIndex = inArray(img, self.pending);
        if (pendingIndex === -1) {
            return;
        } else {
            self.pending.splice(pendingIndex, 1);
        }
        // Store element in loaded images array
        self.loaded.push(img);
        // Keep track of broken and properly loaded images
        self[isBroken ? 'broken' : 'proper'].push(img);
        // Cache image state for future calls
        img[ILID].isBroken = isBroken;
        img[ILID].src = img.src;
        // Trigger progress callback
        setTimeout(function () {
            callbacks.trigger('progress', self, img, isBroken);
        });
        // Call doneLoading
        if (self.images.length === self.loaded.length) {
            setTimeout(doneLoading);
        }
    }

    /**
     * Checks the status of all images.
     *
     * @return {Void}
     */
    function check() {
        // If no images, trigger immediately
        if (!self.images.length) {
            doneLoading();
            return;
        }
        // Actually check the images
        var img;
        for (var i = 0, il = self.images.length; i < il; i++) {
            img = self.images[i];
            img[ILID] = img[ILID] || {};
            // Add image to pending array
            self.pending.push(img);
            // Find out whether this image has been already checked for status.
            // If it was, and src has not changed, call imgLoaded.
            if (img[ILID].isBroken !== undefined && img[ILID].src === img.src) {
                imgLoaded(img, img[ILID].isBroken);
                continue;
            }
            // If complete is true and browser supports natural sizes,
            // try to check for image status manually.
            if (img.complete && img.naturalWidth !== undefined) {
                imgLoaded(img, img.naturalWidth === 0);
                continue;
            }
            // If none of the checks above matched, simulate loading on detached element.
            img[ILID].tmpImg = document.createElement('img');
            bind(img[ILID].tmpImg, EVENTS, proxy(img, imgLoadedHandler));
            img[ILID].tmpImg.src = img.src;
        }
    }

    // Defer the images check to next process tick to give people time to bind progress callbacks.
    setTimeout(check);
    // Set the timeout
    setTimeout(terminate, options.timeout);
    // Return the instance
    return self;
}

// Default options
ImagesLoaded.defaults = {
    timeout: 10000 // Automatically fail images loading when this time has passed.
};

// Expose globally
w.ImagesLoaded = ImagesLoaded;
    function replaceElement(){
        var ParentElement=document.getElementById('doi-link');
        var Array = ParentElement.childNodes;
        //look for the element containing the link
        for(var f=0; f<Array.length; f++){
            if(Array[f].href.indexOf('doi.org/') != -1){
                var newA = document.createElement('a');
                newA.href=Array[f].href.replace('doi.org','sci-hub.tw');
                newA.id='clickme';
                newA.innerHTML='Sci-Hub';
                newA.style = 'background:#3d85c6;background:linear-gradient(#3d85c6,#073763);border-radius:5px;padding:8px 20px;color:#ffffff;display:inline-block;font:normal bold 24px/1 "Calibri", sans-serif;text-align:center;text-shadow:1px 1px #000000;';
                newA.role=('button');
                f=Array.length;
//when the object has been found(there is only one on any page),
//f is increased to exit the 'for' loop.
            }
       };
        //create a new 'DIV' element, append new 'A' element & replace the old 'DIV' element.
        var newDIV=document.createElement('div');
        newDIV.id="newdivelement";
        newDIV.appendChild(newA);
        ParentElement.replaceWith(newDIV);}

window.addEventListener('load', replaceElement);
function startup(ms){
    setTimeout(rune,ms);}

document.addEventListener ('load', ImagesLoaded(документ) .always (запуск (2000)));} (окно));

...