How to Wait for an Element to Exist in JavaScript

Learn about the two ways to wait until a DOM element exists before executing a JavaScript function.

Published Categorized as JavaScript

You can create a function that waits for a DOM element to exist before it executes in JavaScript.

There’s more than one way to achieve this, but the two best are arguably with a recursive function and by using the MutationObserver API.

In this tutorial, I will walk you through both of these methods and show you how to use them to get the job done. I will also walk you through the pros and cons of each, so you can decide which one to use for your needs.

Using a Recursive Function

Let’s use a recursive function with the setInterval method which repeatedly calls a function with a fixed time delay between each call.

This will keep checking if the DOM element in question exists—and if it does, it will stop checking and call the function that you want to execute.

Here’s a simple example of the code:

function waitForElement(selector, callback, waitTime) {
    // Check if an element matching the selector exists in the DOM
    if (document.querySelector(selector)) {
        // If the element is found, invoke the callback function
        callback();
    } else {
        // If the element is not found at first, set up a timeout to recheck after waitTime
        setTimeout(function() {
            // Recursively call waitForElement with the same selector, callback, and waitTime values
            waitForElement(selector, callback, waitTime);
        }, waitTime);
    }
}

We start by defining a function waitForElement, which takes three parameters: selector, callback, and waitTime.

The selector parameter is a string containing one or more CSS selectors separated by commas. callback is the function you want to execute after the element appears. Finally, waitTime represents the delay (in milliseconds) between each attempt to find the element.

Here’s how you might use the updated function:

waitForElement("#myElement", function() {
    console.log("The element now exists!");
}, 500);

In this case, the function will attempt to find an element with the ID #myElement every half second (500 milliseconds). When the element appears, the script logs a message in the console.

You should be careful when using this method, as it could run indefinitely if the element never appears.

Using the MutationObserver API

The MutationObserver API provides developers a way to react to changes in a DOM.

It’s designed to react to changes in the DOM tree and is generally more efficient than the interval checking method, especially for web apps.

Here’s how you can use MutationObserver API to wait for an element:

function waitForElement(selector, callback) {
    // Initialize a mutation observer
    var observer = new MutationObserver(function(mutations, me) {
        // Query for the element
        var element = document.querySelector(selector);
        if (element) {
            callback(element);
            // Once the element has been found, we can stop observing for mutations
            me.disconnect();
            return;
        }
    });

    // Start observing the document with the configured parameters
    observer.observe(document, { childList: true, subtree: true });
}

Let’s break down the steps in the function:

  1. We start by creating a new MutationObserver. This takes a callback function as an argument that will be invoked whenever a mutation is observed. The callback function is given a list of MutationRecords (stored in mutations) and a reference to the observer (stored in me).
  2. Inside the callback function, we use document.querySelector(selector) to try to find the element we’re looking for.
  3. If the element is found (if (element)), we call the callback function with the found element as a parameter, stop observing for mutations (me.disconnect()), and return from the function.
  4. Finally, outside the callback function but still inside waitForElement, we call observer.observe to start observing for mutations. Here, we’re observing the entire document and looking for changes in the childList (nodes added or removed) and subtree (all descendants of the specified node) of the document.

And here’s how you might use the waitForElement function:

waitForElement('#myElement', function(element) {
    console.log("The element now exists!", element);
});

While this method is typically more efficient than the interval checking method, it may still cause performance issues if not used correctly.

For instance, it could run indefinitely if the element never appears.

You may want to add additional conditions to stop observing after a certain period of time or a certain number of attempts.

To do this, you can add a timeout to the waitForElement function by using setTimeout. This will stop the function from running after a specified period of time.

Here’s the code:

function waitForElement(selector, callback, timeout) {
    // Initialize a mutation observer
    var observer = new MutationObserver(function(mutations, me) {
        // Query for the element
        var element = document.querySelector(selector);
        if (element) {
            callback(element);
            // Once we have found the element, we can stop observing for mutations
            me.disconnect();
            return;
        }
    });

    // Start observing the document with the configured parameters
    observer.observe(document, { childList: true, subtree: true });

    // Set a timeout to stop observing after the specified period of time
    setTimeout(function() {
        observer.disconnect();
        console.log(`Element not found within ${timeout}ms. Disconnecting observer.`);
    }, timeout);
}

And here’s how to call your function:

waitForElement('#myElement', function(element) {
    console.log("The element #myElement now exists!", element);
}, 5000); // Timeout after 5,000ms (5 seconds)

By Dim Nikov

Editor of Maker's Aid. Part programmer, part marketer. Making things on the web and helping others do the same since the 2000s. Yes, I had Friendster and Myspace.

Leave a comment

Your email address will not be published. Required fields are marked *