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);
});
}
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With