How to Wait for a DOM element to Exist in JavaScript

avatar
Borislav Hadzhiev

Last updated: Mar 7, 2024
5 min

banner

# How to Wait for a DOM element to Exist in JavaScript

To wait for a DOM element to exist in JavScript:

  1. Use the MutationObserver API to observe the DOM elements in the document.body object.
  2. If an element with the given selector exists, resolve a Promise.
  3. Await the promise to get notified when the element exists.

Here is the HTML for the example.

index.html
<!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>
The code for this article is available on GitHub

And here is the related JavaScript code.

index.js
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 code for this article is available on GitHub

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.

index.js
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.

wait for element to exist using then

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.

index.js
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.

wait for element to exist using async await

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.

index.js
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);

verify the function works as expected

The code for this article is available on GitHub

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.

index.js
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.

index.js
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.

index.js
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.

# Using the setInterval() method to wait for an element to exist

You can also use the setInterval() method to wait for an element to exist.

Here is the HTML for the example.

index.html
<!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>
The code for this article is available on GitHub

And here is the related JavaScript code.

index.js
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 code for this article is available on GitHub

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.

Each time the function is called, we use the 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.

wait for element to exist using setinterval

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.

index.js
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);

testing wait for element to exist setinterval function

The code for this article is available on GitHub

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.

# Using an if statement to exit early if the element exists

Another 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.

index.js
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 code for this article is available on GitHub

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.

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.

Copyright ยฉ 2024 Borislav Hadzhiev