How to wait until an element exists?

后端 未结 19 1740
广开言路
广开言路 2020-11-22 09:14

I\'m working on an Extension in Chrome, and I\'m wondering: what\'s the best way to find out when an element comes into existence? Using plain javascript, with an interval t

相关标签:
19条回答
  • 2020-11-22 09:45

    How about the insertionQuery library?

    insertionQuery uses CSS Animation callbacks attached to the selector(s) specified to run a callback when an element is created. This method allows callbacks to be run whenever an element is created, not just the first time.

    From github:

    Non-dom-event way to catch nodes showing up. And it uses selectors.

    It's not just for wider browser support, It can be better than DOMMutationObserver for certain things.

    Why?

    • Because DOM Events slow down the browser and insertionQuery doesn't
    • Because DOM Mutation Observer has less browser support than insertionQuery
    • Because with insertionQuery you can filter DOM changes using selectors without performance overhead!

    Widespread support!

    IE10+ and mostly anything else (including mobile)

    0 讨论(0)
  • 2020-11-22 09:45

    I usually use this snippet for Tag Manager:

    <script>
    (function exists() {
      if (!document.querySelector('<selector>')) {
        return setTimeout(exists);
      }
      // code when element exists
    })();  
    </script>
    
    0 讨论(0)
  • 2020-11-22 09:46

    I was having this same problem, so I went ahead and wrote a plugin for it.

    $(selector).waitUntilExists(function);

    Code:

    ;(function ($, window) {
    
    var intervals = {};
    var removeListener = function(selector) {
    
        if (intervals[selector]) {
    
            window.clearInterval(intervals[selector]);
            intervals[selector] = null;
        }
    };
    var found = 'waitUntilExists.found';
    
    /**
     * @function
     * @property {object} jQuery plugin which runs handler function once specified
     *           element is inserted into the DOM
     * @param {function|string} handler 
     *            A function to execute at the time when the element is inserted or 
     *            string "remove" to remove the listener from the given selector
     * @param {bool} shouldRunHandlerOnce 
     *            Optional: if true, handler is unbound after its first invocation
     * @example jQuery(selector).waitUntilExists(function);
     */
    
    $.fn.waitUntilExists = function(handler, shouldRunHandlerOnce, isChild) {
    
        var selector = this.selector;
        var $this = $(selector);
        var $elements = $this.not(function() { return $(this).data(found); });
    
        if (handler === 'remove') {
    
            // Hijack and remove interval immediately if the code requests
            removeListener(selector);
        }
        else {
    
            // Run the handler on all found elements and mark as found
            $elements.each(handler).data(found, true);
    
            if (shouldRunHandlerOnce && $this.length) {
    
                // Element was found, implying the handler already ran for all 
                // matched elements
                removeListener(selector);
            }
            else if (!isChild) {
    
                // If this is a recurring search or if the target has not yet been 
                // found, create an interval to continue searching for the target
                intervals[selector] = window.setInterval(function () {
    
                    $this.waitUntilExists(handler, shouldRunHandlerOnce, true);
                }, 500);
            }
        }
    
        return $this;
    };
    
    }(jQuery, window));
    
    0 讨论(0)
  • 2020-11-22 09:49

    You can do

    $('#yourelement').ready(function() {
    
    });
    

    Please note that this will only work if the element is present in the DOM when being requested from the server. If the element is being dynamically added via JavaScript, it will not work and you may need to look at the other answers.

    0 讨论(0)
  • 2020-11-22 09:50

    A solution returning a Promise and allowing to use a timeout (compatible IE 11+).

    For a single element (type Element):

    "use strict";
    
    function waitUntilElementLoaded(selector) {
        var timeout = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
    
        var start = performance.now();
        var now = 0;
    
        return new Promise(function (resolve, reject) {
            var interval = setInterval(function () {
                var element = document.querySelector(selector);
    
                if (element instanceof Element) {
                    clearInterval(interval);
    
                    resolve();
                }
    
                now = performance.now();
    
                if (now - start >= timeout) {
                    reject("Could not find the element " + selector + " within " + timeout + " ms");
                }
            }, 100);
        });
    }
    

    For multiple elements (type NodeList):

    "use strict";
    
    function waitUntilElementsLoaded(selector) {
        var timeout = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
    
        var start = performance.now();
        var now = 0;
    
        return new Promise(function (resolve, reject) {
            var interval = setInterval(function () {
                var elements = document.querySelectorAll(selector);
    
                if (elements instanceof NodeList) {
                    clearInterval(interval);
    
                    resolve(elements);
                }
    
                now = performance.now();
    
                if (now - start >= timeout) {
                    reject("Could not find elements " + selector + " within " + timeout + " ms");
                }
            }, 100);
        });
    }
    

    Examples:

    waitUntilElementLoaded('#message', 800).then(function(element) {
        // element found and available
    
        element.innerHTML = '...';
    }).catch(function() {
        // element not found within 800 milliseconds
    });
    
    waitUntilElementsLoaded('.message', 10000).then(function(elements) {
        for(const element of elements) {
            // ....
        }
    }).catch(function(error) {
        // elements not found withing 10 seconds
    });
    

    Works for both a list of elements and a single element.

    0 讨论(0)
  • 2020-11-22 09:51

    You can listen to DOMNodeInserted or DOMSubtreeModified events which fire whenever a new element is added to the DOM.

    There is also LiveQuery jQuery plugin which would detect when a new element is created:

    $("#future_element").livequery(function(){
        //element created
    });
    
    0 讨论(0)
提交回复
热议问题