It looks like @zahanm answer won't work if you replace the entire document HTML, which was what I needed. So I had to draw back to setInterval. Based on @shawndumas answer I've created a more modern approach using Promise:
function waitUntilSelectorExist(selector, interval = 100, timeout = 20000) {
let intervalId;
let elapsed = 0;
let promise = new Promise(function(resolve, reject) {
intervalId = setInterval(() => {
let element = document.querySelector(selector);
if (element !== null) {
clearInterval(intervalId);
resolve(element);
}
elapsed += interval;
if (elapsed > timeout) {
clearInterval(intervalId);
reject(`The selector ${selector} did not enter within the ${timeout}ms frame!`);
}
}, interval);
});
return promise;
}
Also I use querySelector instead, so you can search for whatever selector you like, either .class, #id, etc.
If the function ever find the querySelector within the document the Promise will resolve, so you can use it with the new await keyword.
(async() => {
try {
await waitUntilSelectorExist("#test");
console.log("Do stuff!");
} catch (error) {
console.warn(error); // timeout!
}
})();
Of course you could also wrap around MutationObserver within a Promise if you are really just changing some elements within the document:
function waitUntilSelectorExist(selector) {
let MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
let promise = new Promise(function(resolve) {
let observer;
observer = new MutationObserver(function(mutations) {
let element = document.querySelector(selector);
if (element !== null) {
observer.disconnect();
resolve();
}
});
observer.observe(document.body, { childList: true, subtree: true });
});
return promise;
}