简体   繁体   中英

Recursively and asynchronously check if html element exists

I am interacting with a webpage whereby some of the deeper nested elements within sub-frames load even after the document is ready - ie they rely on pending ajax/api requests that are yet to be completed when the document is loaded.

My intention is to wait until these elements exist prior to doing anything with them. I can do this using setTimeout and wait for an arbitrary amount of time before doing anything eg

setTimeout(function() {
      $("#better_content .binder__toc").append(
        "<h4>Experiments</h4><ul><li>This</li><li>is</li><li>a</li><li>test</li></ul>"
      );
    }, 5000);

However, it would be nice in a recursive and asynchronous (non-blocking) manner to keep checking for said element ("#better_content .binder__toc") until undefined or null is not returned ie the element exists. I have tried to do this using Promises. A simple example with a counter is as follows:

static waitForElement = counter => {

   counter++

    return new Promise((res, rej) => {
      if (counter < 5) {
        this.waitForElement(counter)
          .then(function() {
            res("complete");
          })
          .catch(rej);
      }
      res("complete");
    });
  };

this.waitForElement(counter)
      .then(a => console.log(a))
      .catch(a => console.log(a));

The above resolves successfully and appears to be non-blocking. However, if I replace the counter for the element selector as below:

static waitForElement = selector => {        

    let found = $(document).find(selector)[0];        
    console.log(found);

    return new Promise((res, rej) => {
      if (found === undefined) {
        this.waitForElement(selector)
          .then(function() {
            res("found");
          })
          .catch(rej);
      }
      res("found");
    });
  };


this.waitForElement("#better_content .binder__toc")
      .then(a => {
        console.log(a);
        $("#better_content .binder__toc").append(
          "<h4>Experiments</h4><ul><li>This</li><li>is</li><li>a</li><li>test</li></ul>"
        );
      })
      .catch(a => console.log(a));

Then this never seems to resolve successfully and really slows down the webpage - I assume as it is blocking the main thread still.

Any suggestions on how I can correct or understand what is going on here would be more than welcome.

You can check for changes in an element, or children of an element, with the Mutation Observer API . The API fires a callback in which you can assert your logic to act when a certain change in the element is happened. In your case you'll want to listen on the container where the element is appended to with the fetch request.

It is possible to listen for certain changes, like attribute changes, or in your case changes in the children. In the configuration add childList: true which will indicate that you want to do something whenever a child is added or removed.

This way you don't have to check in if the element exists.
Check out the example below to see your example in action.

 const target = document.getElementById('container'); const config = { childList: true }; const observer = new MutationObserver((mutations, observer) => { /** * Loop through all changes */ mutations.forEach(mutation => { /** * Only act if it is a change in the children. */ if (mutation.type !== 'childList') { return; } /** * Loop through the added elements and check if * it is the correct element. Then add HTML to * the newly added element. */ mutation.addedNodes.forEach(node => { if (node.id === 'content') { const html = ` <h4>Experiments</h4> <ul> <li>This</li> <li>is</li> <li>a</li ><li>test</li> </ul>`; node.innerHTML = html; } }); // Stop observing. observer.disconnect(); }); }); // Observe the children in container. observer.observe(target, config); // Add content element after 2 seconds. setTimeout(function() { const content = document.createElement('div'); content.id = 'content'; target.append(content); }, 2000);
 <div id="container"></div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM