How to wait until an element exists?

后端 未结 19 1763
广开言路
广开言路 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:29

    Here is a core JavaScript function to wait for the display of an element (well, its insertion into the DOM to be more accurate).

    // Call the below function
    waitForElementToDisplay("#div1",function(){alert("Hi");},1000,9000);
    
    function waitForElementToDisplay(selector, callback, checkFrequencyInMs, timeoutInMs) {
      var startTimeInMs = Date.now();
      (function loopSearch() {
        if (document.querySelector(selector) != null) {
          callback();
          return;
        }
        else {
          setTimeout(function () {
            if (timeoutInMs && Date.now() - startTimeInMs > timeoutInMs)
              return;
            loopSearch();
          }, checkFrequencyInMs);
        }
      })();
    }
    

    This call will look for the HTML tag whose id="div1" every 1000 milliseconds. If the element is found, it will display an alert message Hi. If no element is found after 9000 milliseconds, this function stops its execution.

    Parameters:

    1. selector: String : This function looks for the element ${selector}.
    2. callback: Function : This is a function that will be called if the element is found.
    3. checkFrequencyInMs: Number : This function checks whether this element exists every ${checkFrequencyInMs} milliseconds.
    4. timeoutInMs : Number : Optional. This function stops looking for the element after ${timeoutInMs} milliseconds.

    NB : Selectors are explained at https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector

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

    Here's a function that acts as a thin wrapper around MutationObserver. The only requirement is that the browser support MutationObserver; there is no dependency on JQuery. Run the snippet below to see a working example.

    function waitForMutation(parentNode, isMatchFunc, handlerFunc, observeSubtree, disconnectAfterMatch) {
      var defaultIfUndefined = function(val, defaultVal) {
        return (typeof val === "undefined") ? defaultVal : val;
      };
    
      observeSubtree = defaultIfUndefined(observeSubtree, false);
      disconnectAfterMatch = defaultIfUndefined(disconnectAfterMatch, false);
    
      var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
          if (mutation.addedNodes) {
            for (var i = 0; i < mutation.addedNodes.length; i++) {
              var node = mutation.addedNodes[i];
              if (isMatchFunc(node)) {
                handlerFunc(node);
                if (disconnectAfterMatch) observer.disconnect();
              };
            }
          }
        });
      });
    
      observer.observe(parentNode, {
        childList: true,
        attributes: false,
        characterData: false,
        subtree: observeSubtree
      });
    }
    
    // Example
    waitForMutation(
      // parentNode: Root node to observe. If the mutation you're looking for
      // might not occur directly below parentNode, pass 'true' to the
      // observeSubtree parameter.
      document.getElementById("outerContent"),
      // isMatchFunc: Function to identify a match. If it returns true,
      // handlerFunc will run.
      // MutationObserver only fires once per mutation, not once for every node
      // inside the mutation. If the element we're looking for is a child of
      // the newly-added element, we need to use something like
      // node.querySelector() to find it.
      function(node) {
        return node.querySelector(".foo") !== null;
      },
      // handlerFunc: Handler.
      function(node) {
        var elem = document.createElement("div");
        elem.appendChild(document.createTextNode("Added node (" + node.innerText + ")"));
        document.getElementById("log").appendChild(elem);
      },
      // observeSubtree
      true,
      // disconnectAfterMatch: If this is true the hanlerFunc will only run on
      // the first time that isMatchFunc returns true. If it's false, the handler
      // will continue to fire on matches.
      false);
    
    // Set up UI. Using JQuery here for convenience.
    
    $outerContent = $("#outerContent");
    $innerContent = $("#innerContent");
    
    $("#addOuter").on("click", function() {
      var newNode = $("<div><span class='foo'>Outer</span></div>");
      $outerContent.append(newNode);
    });
    $("#addInner").on("click", function() {
      var newNode = $("<div><span class='foo'>Inner</span></div>");
      $innerContent.append(newNode);
    });
    .content {
      padding: 1em;
      border: solid 1px black;
      overflow-y: auto;
    }
    #innerContent {
      height: 100px;
    }
    #outerContent {
      height: 200px;
    }
    #log {
      font-family: Courier;
      font-size: 10pt;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <h2>Create some mutations</h2>
    <div id="main">
      <button id="addOuter">Add outer node</button>
      <button id="addInner">Add inner node</button>
      <div class="content" id="outerContent">
        <div class="content" id="innerContent"></div>
      </div>
    </div>
    <h2>Log</h2>
    <div id="log"></div>

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

    I used this approach to wait for an element to appear so I can execute the other functions after that.

    Let's say doTheRestOfTheStuff(parameters) function should only be called after the element with ID the_Element_ID appears or finished loading, we can use,

    var existCondition = setInterval(function() {
     if ($('#the_Element_ID').length) {
        console.log("Exists!");
        clearInterval(existCondition);
        doTheRestOfTheStuff(parameters);
     }
    }, 100); // check every 100ms
    
    0 讨论(0)
  • 2020-11-22 09:30

    Simply add the selector you want. Once the element is found you can have access to in the callback function.

    const waitUntilElementExists = (selector, callback) => {
    const el = document.querySelector(selector);
    
    if (el){
        return callback(el);
    }
    
    setTimeout(() => waitUntilElementExists(selector, callback), 500);
    }
    
    waitUntilElementExists('.wait-for-me', (el) => console.log(el));
    
    0 讨论(0)
  • 2020-11-22 09:31

    Here's a pure Javascript function which allows you to wait for anything. Set the interval longer to take less CPU resource.

    /**
     * @brief Wait for something to be ready before triggering a timeout
     * @param {callback} isready Function which returns true when the thing we're waiting for has happened
     * @param {callback} success Function to call when the thing is ready
     * @param {callback} error Function to call if we time out before the event becomes ready
     * @param {int} count Number of times to retry the timeout (default 300 or 6s)
     * @param {int} interval Number of milliseconds to wait between attempts (default 20ms)
     */
    function waitUntil(isready, success, error, count, interval){
        if (count === undefined) {
            count = 300;
        }
        if (interval === undefined) {
            interval = 20;
        }
        if (isready()) {
            success();
            return;
        }
        // The call back isn't ready. We need to wait for it
        setTimeout(function(){
            if (!count) {
                // We have run out of retries
                if (error !== undefined) {
                    error();
                }
            } else {
                // Try again
                waitUntil(isready, success, error, count -1, interval);
            }
        }, interval);
    }
    

    To call this, for example in jQuery, use something like:

    waitUntil(function(){
        return $('#myelement').length > 0;
    }, function(){
        alert("myelement now exists");
    }, function(){
        alert("I'm bored. I give up.");
    });
    
    0 讨论(0)
  • 2020-11-22 09:32

    This is a simple solution for those who are used to promises and don't want to use any third party libs or timers.

    I have been using it in my projects for a while

    function waitForElm(selector) {
        return new Promise(resolve => {
            if (document.querySelector(selector)) {
                return resolve(document.querySelector(selector));
            }
    
            const observer = new MutationObserver(mutations => {
                if (document.querySelector(selector)) {
                    resolve(document.querySelector(selector));
                    observer.disconnect();
                }
            });
    
            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        });
    }
    

    To use it:

    waitForElm('.some-class').then(elm => console.log(elm.textContent));
    

    or with async/await

    const elm = await waitForElm('.some-classs')
    
    0 讨论(0)
提交回复
热议问题