Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to wait for element with timeouts in javascript

I am looking for a way to wait for element to be displayed in DOM and throw a timeout exception when it is not displayed in a give time. How can I accomplish that? I have this take, but sure it will not work.

function waitForElement(element, timeout) {
    return new Promise((resolve, reject) => {
        const t = setTimeout(() => {
            let queryInterval = setInterval(() => {
                const isElementVisible = document.contains(element);
                if (!isElementVisible) {
                    clearInterval(queryInterval);
                    clearTimeout(t);
                    resolve(true);
                }
            }, 10);
            console.log("Timeout!");
            clearTimeout(t);
            reject("Time Out!");
        }, timeout);
    });
}
like image 216
Nodir Nasirov Avatar asked Jun 18 '26 16:06

Nodir Nasirov


1 Answers

Check out the mutation observer api.

Here's a sample implementation that returns a Promise that either resolves with the added node or rejects if the node doesn't appear within the allotted time.

It accomplishes this by setting up a MutationObserver to watch for a node to appear that passes the given testFn. It sets a timeout that disconnects the observer and rejects after a given amount of time. If the observer sees the node appear before the timeout expires it clears the timeout and resolves with the node.

// Waits for a node that passes testFn to appear and returns it.
// Gives up and rejects after 'wait' ms.
// Returns a Promise that resolves with the found node.
const waitForElement = (testFn, wait = 1000) => {

  return new Promise((resolve, reject) => {

    // create an observer using onDomChange (defined below)
    const observer = new MutationObserver(onDomChange);

    // watch the container for childList changes
    observer.observe(container, {
      childList: true
    });

    // set a countdown to give up and reject
    const timeout = setTimeout(
      () => {
        // when the timeout expires, stop watching…
        observer.disconnect();
        // and reject
        reject('Never showed up.')
      },
      wait // how long to wait before rejecting
    );

    // the callback for observed dom changes
    function onDomChange(mutations, observer) {
      // find an addedNode that passes the testFn in the mutations
      const node = mutations.map(m => [...m.addedNodes]).flat().find(testFn);

      // if we find a match…
      if (node) {
        // stop the rejection timeout
        clearTimeout(timeout);

        // stop observing
        observer.disconnect();

        // and resolve with the found node
        resolve(node);
      }
    }
  });
}

// convenience for creating a div with the given text
const d = (text) => {
  const el = document.createElement('div');
  el.className = 'bananas';
  el.innerText = text;
  return el;
}

// test the waitForElement with the given delay
async function go(delay) {
  const container = document.getElementById('container');

  // wait for the specified duration before adding a div with the delay as the text
  setTimeout(() => container.appendChild(d(delay)), delay);

  try {
    // wait for the element to appear
    const el = await waitForElement(n => n.innerText.endsWith(delay));
    // append some text to the content
    el.innerText += ' - found it.'
    // add a css class
    el.classList.add('found');
  } catch (e) {
    // log an error if it didn't appear in time
    console.error(`rejected for ${delay}`);
  }
}

// default wait time is 1000ms, so…
go(300); // under the 1000ms wait time. finds it.
go(5000); // takes too long. rejects
go(100); // finds it.
go(600); // finds it.
go(3000); // rejects
.bananas {
  margin-bottom: .5em;
  padding: 0.25em;
  background: #ffff99;
}

.found {
  background: tomato;
  color: bisque;
}
<div id="container"></div>
like image 56
ray hatfield Avatar answered Jun 20 '26 07:06

ray hatfield



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!