Last updated: Mar 7, 2024
Reading timeยท5 min
To wait for a DOM element to exist in JavScript:
MutationObserver
API to observe the DOM elements in the
document.body
object.Here is the HTML for the example.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> </head> <body> <div id="box">bobbyhadz.com</div> <script src="index.js"></script> </body> </html>
And here is the related JavaScript code.
function waitForElementToExist(selector) { return new Promise(resolve => { if (document.querySelector(selector)) { return resolve(document.querySelector(selector)); } const observer = new MutationObserver(() => { if (document.querySelector(selector)) { resolve(document.querySelector(selector)); observer.disconnect(); } }); observer.observe(document.body, { subtree: true, childList: true, }); }); } // ๐๏ธ using the function waitForElementToExist('#box').then(element => { console.log('The element exists', element); });
The function takes a selector (e.g. .my-class
or #my-id
) and resolves a
Promise once a DOM element with the given selector exists.
One way to use the function is to use the .then()
syntax.
waitForElementToExist('#box').then(element => { console.log('The element exists', element); });
An element with an id
of box
exists in the DOM, so the message gets printed
to the console.
Make sure to pass the correct CSS selector to the document.querySelector() method.
For example, if you need to select the first element with a given class, use a
selector such as .my-class
.
You can also use the async/await
syntax when calling the function.
async function doWork() { const element = await waitForElementToExist('#box'); console.log('Element:', element); } doWork();
The await
keyword waits for the Promise to resolve.
The output of running the function is the same.
Let's look at an example that better illustrates how the function works.
I'm going to use the document.createElement() method to create a DOM element after a delay so we can be sure that the function works as expected.
function waitForElementToExist(selector) { return new Promise(resolve => { if (document.querySelector(selector)) { return resolve(document.querySelector(selector)); } const observer = new MutationObserver(() => { if (document.querySelector(selector)) { resolve(document.querySelector(selector)); observer.disconnect(); } }); observer.observe(document.body, { subtree: true, childList: true, }); }); } // ๐๏ธ using the function waitForElementToExist('#new-element').then(element => { console.log('Element:', element); }); // ๐๏ธ Create a new element and add it to the DOM after 2 seconds setTimeout(() => { const newElement = document.createElement('div'); newElement.id = 'new-element'; newElement.textContent = 'New element'; document.body.append(newElement); }, 2000);
We used the setTimeout() function to add an element to the DOM after a delay of 2 seconds.
The short clip shows that the function works as expected.
We used the MutationObserver() constructor to watch for changes in the DOM tree.
const observer = new MutationObserver(() => { if (document.querySelector(selector)) { resolve(document.querySelector(selector)); observer.disconnect(); } });
The callback function we passed to the MutationObserver()
constructor is
called on each DOM change that qualifies given the observed subtree.
observer.observe(document.body, { subtree: true, childList: true, });
We
observe
the entire document.body
. This includes the entire subtree of nodes and the
descendants.
Once an element with the given selector exists in the DOM, the callback function
we passed to the MutationObserver()
constructor gets called.
We resolve the promise with the element and disconnect the observer to stop watching for mutations.
If an element with the given selector exists immediately after loading the page,
the first if
block runs and we immediately resolve the promise with the
element.
if (document.querySelector(selector)) { return resolve(document.querySelector(selector)); }
This is the most efficient approach to wait for an element to exist in the DOM.
setInterval()
method to wait for an element to existYou can also use the setInterval() method to wait for an element to exist.
Here is the HTML for the example.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> </head> <body> <div id="box">bobbyhadz.com</div> <script src="index.js"></script> </body> </html>
And here is the related JavaScript code.
function waitForElementToExist(selector, callback) { const intervalID = setInterval(() => { if (document.querySelector(selector)) { console.log('The element exists'); clearInterval(intervalID); callback(document.querySelector(selector)); } }, 100); } waitForElementToExist('#box', function doWork(element) { console.log('Element:', element); });
The setInterval()
method takes a function and a delay (in milliseconds) as
parameters.
The setInterval()
method in the example calls the provided function every 100
milliseconds but you can adjust the delay if necessary.
document.querySelector()
method to look for a DOM element with the given selector.If the condition is met, we use the
clearInterval()
method to cancel the repeating action that we previously set up using
setInterval
.
We also call a callback function that was provided by the caller of the
waitForElementToExist
function.
The element exists in the DOM, so the success message gets printed to the console.
Let's test the function with an element that doesn't exist initially.
We'll create an element after the delay, just like in the previous subheading.
function waitForElementToExist(selector, callback) { const intervalID = setInterval(() => { if (document.querySelector(selector)) { console.log('The element exists'); clearInterval(intervalID); callback(document.querySelector(selector)); } }, 500); } waitForElementToExist('#new-element', function doWork(element) { console.log('Element:', element); }); setTimeout(() => { const newElement = document.createElement('div'); newElement.id = 'new-element'; newElement.textContent = 'New element'; document.body.append(newElement); }, 2000);
We used the setTimeout
method to add a new element to the DOM after 2 seconds.
The setInterval()
method keeps calling its function every 500 milliseconds
until it finds the DOM element with the matching selector (or infinitely if a
DOM element with the selector isn't found).
This approach is quite simple as it doesn't use external, modern APIs such as
MutationObserver
.
However, it is also a bit less performant.
if
statement to exit early if the element existsAnother thing that should be noted is that the first time the function we pass
to setInterval
is called after delay
milliseconds.
For example, the function we passed to setInterval()
in the example is first
called after a delay of 500
milliseconds.
This is very inefficient, especially if the DOM element exists immediately after the page loads.
You can get around this by using an if
statement to check whether the element
exists before setting up the interval.
function waitForElementToExist(selector, callback) { if (document.querySelector(selector)) { console.log('The element exists'); return callback(document.querySelector(selector)); } const intervalID = setInterval(() => { if (document.querySelector(selector)) { console.log('The element exists'); clearInterval(intervalID); callback(document.querySelector(selector)); } }, 500); } waitForElementToExist('#box', function doWork(element) { console.log('Element:', element); });
The first if
statement checks if the DOM element with the given selector
exists immediately after the page loads.
If the condition is met, we call the callback function with the element and exit
the waitForElementToExist
function using a return
statement.
Which approach you pick is a matter of personal preference. I'd use the
MutationObserver
API as it is quite performant and the complexity of using it
is abstracted in a reusable function.
You can learn more about the related topics by checking out the following tutorials: