How to wait until an element exists?

I'm working on an Extension in Chrome, and I'm wondering: what's the best way to find out when an element comes into existence? Using plain javascript, with an interval that checks until an element exists, or does jQuery have some easy way to do this?

8

23 Answers

DOMNodeInserted is being deprecated, along with the other DOM mutation events, because of performance issues - the recommended approach is to use a MutationObserver to watch the DOM. It's only supported in newer browsers though, so you should fall back onto DOMNodeInserted when MutationObserver isn't available.

let observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (!mutation.addedNodes) return for (let i = 0; i < mutation.addedNodes.length; i++) { // do things to your newly added nodes here let node = mutation.addedNodes[i] } })
})
observer.observe(document.body, { childList: true , subtree: true , attributes: false , characterData: false
})
// stop watching using:
observer.disconnect()
13

Here is a simple solution using the MutationObserver api.

  1. No jQuery
  2. No Timer
  3. No third party libraries
  4. Promise based and works well with async/await

I have used it in several projects.

function waitForElm(selector) { return new Promise(resolve => { if (document.querySelector(selector)) { return resolve(document.querySelector(selector)); } const observer = new MutationObserver(mutations => { if (document.querySelector(selector)) { resolve(document.querySelector(selector)); observer.disconnect(); } }); observer.observe(document.body, { childList: true, subtree: true }); });
}

To use it:

waitForElm('.some-class').then((elm) => { console.log('Element is ready'); console.log(elm.textContent);
});

Or with async/await:

const elm = await waitForElm('.some-class');
7

Here is a core JavaScript function to wait for the display of an element (well, its insertion into the DOM to be more accurate).

// Call the below function
waitForElementToDisplay("#div1",function(){alert("Hi");},1000,9000);
function waitForElementToDisplay(selector, callback, checkFrequencyInMs, timeoutInMs) { var startTimeInMs = Date.now(); (function loopSearch() { if (document.querySelector(selector) != null) { callback(); return; } else { setTimeout(function () { if (timeoutInMs && Date.now() - startTimeInMs > timeoutInMs) return; loopSearch(); }, checkFrequencyInMs); } })();
}

This call will look for the HTML tag whose id="div1" every 1000 milliseconds. If the element is found, it will display an alert message Hi. If no element is found after 9000 milliseconds, this function stops its execution.

Parameters:

  1. selector: String : This function looks for the element ${selector}.
  2. callback: Function : This is a function that will be called if the element is found.
  3. checkFrequencyInMs: Number : This function checks whether this element exists every ${checkFrequencyInMs} milliseconds.
  4. timeoutInMs : Number : Optional. This function stops looking for the element after ${timeoutInMs} milliseconds.

NB : Selectors are explained at

11

I was having this same problem, so I went ahead and wrote a plugin for it.

$(selector).waitUntilExists(function);

Code:

;(function ($, window) {
var intervals = {};
var removeListener = function(selector) { if (intervals[selector]) { window.clearInterval(intervals[selector]); intervals[selector] = null; }
};
var found = 'waitUntilExists.found';
/** * @function * @property {object} jQuery plugin which runs handler function once specified * element is inserted into the DOM * @param {function|string} handler * A function to execute at the time when the element is inserted or * string "remove" to remove the listener from the given selector * @param {bool} shouldRunHandlerOnce * Optional: if true, handler is unbound after its first invocation * @example jQuery(selector).waitUntilExists(function); */
$.fn.waitUntilExists = function(handler, shouldRunHandlerOnce, isChild) { var selector = this.selector; var $this = $(selector); var $elements = $this.not(function() { return $(this).data(found); }); if (handler === 'remove') { // Hijack and remove interval immediately if the code requests removeListener(selector); } else { // Run the handler on all found elements and mark as found $elements.each(handler).data(found, true); if (shouldRunHandlerOnce && $this.length) { // Element was found, implying the handler already ran for all // matched elements removeListener(selector); } else if (!isChild) { // If this is a recurring search or if the target has not yet been // found, create an interval to continue searching for the target intervals[selector] = window.setInterval(function () { $this.waitUntilExists(handler, shouldRunHandlerOnce, true); }, 500); } } return $this;
};
}(jQuery, window));
11

I used this approach to wait for an element to appear so I can execute the other functions after that.

Let's say doTheRestOfTheStuff(parameters) function should only be called after the element with ID the_Element_ID appears or finished loading, we can use,

var existCondition = setInterval(function() { if ($('#the_Element_ID').length) { console.log("Exists!"); clearInterval(existCondition); doTheRestOfTheStuff(parameters); }
}, 100); // check every 100ms
1

I think that still there isnt any answer here with easy and readable working example. Use MutationObserver interface to detect DOM changes, like this:

var observer = new MutationObserver(function(mutations) { if ($("p").length) { console.log("Exist, lets do something"); observer.disconnect(); //We can disconnect observer once the element exist if we dont want observe more changes in the DOM }
});
// Start observing
observer.observe(document.body, { //document.body is node target to observe childList: true, //This is a must have for the observer with subtree subtree: true //Set to true if changes must also be observed in descendants.
});
$(document).ready(function() { $("button").on("click", function() { $("p").remove(); setTimeout(function() { $("#newContent").append("<p>New element</p>"); }, 2000); });
});
<script src=""></script>
<button>New content</button>
<div></div>

Note: Spanish Mozilla docs about MutationObserver are more detailed if you want more information.

1

You can listen to DOMNodeInserted or DOMSubtreeModified events which fire whenever a new element is added to the DOM.

There is also LiveQuery jQuery plugin which would detect when a new element is created:

$("#future_element").livequery(function(){ //element created
});
3

Update

Below there is an updated version that works with promises. It also "stops" if a specific number of tries is reached.

function _waitForElement(selector, delay = 50, tries = 100) { const element = document.querySelector(selector); if (!window[`__${selector}`]) { window[`__${selector}`] = 0; window[`__${selector}__delay`] = delay; window[`__${selector}__tries`] = tries; } function _search() { return new Promise((resolve) => { window[`__${selector}`]++; setTimeout(resolve, window[`__${selector}__delay`]); }); } if (element === null) { if (window[`__${selector}`] >= window[`__${selector}__tries`]) { window[`__${selector}`] = 0; return Promise.resolve(null); } return _search().then(() => _waitForElement(selector)); } else { return Promise.resolve(element); } }

Usage is very simple, to use it with await just make sure you're within anasync function:

const start = (async () => { const $el = await _waitForElement(`.my-selector`); console.log($el);
})();

Outdated version

Simply add the selector you want. Once the element is found you can have access to in the callback function.

const waitUntilElementExists = (selector, callback) => {
const el = document.querySelector(selector);
if (el){ return callback(el);
}
setTimeout(() => waitUntilElementExists(selector, callback), 500);
}
waitUntilElementExists('.wait-for-me', (el) => console.log(el));
6

You can do

$('#yourelement').ready(function() {
});

Please note that this will only work if the element is present in the DOM when being requested from the server. If the element is being dynamically added via JavaScript, it will not work and you may need to look at the other answers.

6

For a simple approach using jQuery I've found this to work well:

 // Wait for element to exist. function elementLoaded(el, cb) { if ($(el).length) { // Element is now loaded. cb($(el)); } else { // Repeat every 500ms. setTimeout(function() { elementLoaded(el, cb) }, 500); } }; elementLoaded('.element-selector', function(el) { // Element is ready to use. el.click(function() { alert("You just clicked a dynamically inserted element"); }); });

Here we simply check every 500ms to see whether the element is loaded, when it is, we can use it.

This is especially useful for adding click handlers to elements which have been dynamically added to the document.

How about the insertionQuery library?

insertionQuery uses CSS Animation callbacks attached to the selector(s) specified to run a callback when an element is created. This method allows callbacks to be run whenever an element is created, not just the first time.

From github:

Non-dom-event way to catch nodes showing up. And it uses selectors.

It's not just for wider browser support, It can be better than DOMMutationObserver for certain things.

Why?

  • Because DOM Events slow down the browser and insertionQuery doesn't
  • Because DOM Mutation Observer has less browser support than insertionQuery
  • Because with insertionQuery you can filter DOM changes using selectors without performance overhead!

Widespread support!

IE10+ and mostly anything else (including mobile)

0

Here's a function that acts as a thin wrapper around MutationObserver. The only requirement is that the browser support MutationObserver; there is no dependency on JQuery. Run the snippet below to see a working example.

function waitForMutation(parentNode, isMatchFunc, handlerFunc, observeSubtree, disconnectAfterMatch) { var defaultIfUndefined = function(val, defaultVal) { return (typeof val === "undefined") ? defaultVal : val; }; observeSubtree = defaultIfUndefined(observeSubtree, false); disconnectAfterMatch = defaultIfUndefined(disconnectAfterMatch, false); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.addedNodes) { for (var i = 0; i < mutation.addedNodes.length; i++) { var node = mutation.addedNodes[i]; if (isMatchFunc(node)) { handlerFunc(node); if (disconnectAfterMatch) observer.disconnect(); }; } } }); }); observer.observe(parentNode, { childList: true, attributes: false, characterData: false, subtree: observeSubtree });
}
// Example
waitForMutation( // parentNode: Root node to observe. If the mutation you're looking for // might not occur directly below parentNode, pass 'true' to the // observeSubtree parameter. document.getElementById("outerContent"), // isMatchFunc: Function to identify a match. If it returns true, // handlerFunc will run. // MutationObserver only fires once per mutation, not once for every node // inside the mutation. If the element we're looking for is a child of // the newly-added element, we need to use something like // node.querySelector() to find it. function(node) { return node.querySelector(".foo") !== null; }, // handlerFunc: Handler. function(node) { var elem = document.createElement("div"); elem.appendChild(document.createTextNode("Added node (" + node.innerText + ")")); document.getElementById("log").appendChild(elem); }, // observeSubtree true, // disconnectAfterMatch: If this is true the hanlerFunc will only run on // the first time that isMatchFunc returns true. If it's false, the handler // will continue to fire on matches. false);
// Set up UI. Using JQuery here for convenience.
$outerContent = $("#outerContent");
$innerContent = $("#innerContent");
$("#addOuter").on("click", function() { var newNode = $("<div><span class='foo'>Outer</span></div>"); $outerContent.append(newNode);
});
$("#addInner").on("click", function() { var newNode = $("<div><span class='foo'>Inner</span></div>"); $innerContent.append(newNode);
});
.content { padding: 1em; border: solid 1px black; overflow-y: auto;
}
#innerContent { height: 100px;
}
#outerContent { height: 200px;
}
#log { font-family: Courier; font-size: 10pt;
}
<script src=""></script>
<h2>Create some mutations</h2>
<div> <button>Add outer node</button> <button>Add inner node</button> <div> <div></div> </div>
</div>
<h2>Log</h2>
<div></div>

Here's a Promise-returning solution in vanilla Javascript (no messy callbacks). By default it checks every 200ms.

function waitFor(selector) { return new Promise(function (res, rej) { waitForElementToDisplay(selector, 200); function waitForElementToDisplay(selector, time) { if (document.querySelector(selector) != null) { res(document.querySelector(selector)); } else { setTimeout(function () { waitForElementToDisplay(selector, time); }, time); } } });
}

Here's a pure Javascript function which allows you to wait for anything. Set the interval longer to take less CPU resource.

/** * @brief Wait for something to be ready before triggering a timeout * @param {callback} isready Function which returns true when the thing we're waiting for has happened * @param {callback} success Function to call when the thing is ready * @param {callback} error Function to call if we time out before the event becomes ready * @param {int} count Number of times to retry the timeout (default 300 or 6s) * @param {int} interval Number of milliseconds to wait between attempts (default 20ms) */
function waitUntil(isready, success, error, count, interval){ if (count === undefined) { count = 300; } if (interval === undefined) { interval = 20; } if (isready()) { success(); return; } // The call back isn't ready. We need to wait for it setTimeout(function(){ if (!count) { // We have run out of retries if (error !== undefined) { error(); } } else { // Try again waitUntil(isready, success, error, count -1, interval); } }, interval);
}

To call this, for example in jQuery, use something like:

waitUntil(function(){ return $('#myelement').length > 0;
}, function(){ alert("myelement now exists");
}, function(){ alert("I'm bored. I give up.");
});

You can try this:

const wait_until_element_appear = setInterval(() => { if ($(element).length !== 0) { // some code clearInterval(wait_until_element_appear); }
}, 0);

This solution works very good for me

1

The observe function below will allow you to listen to elements via a selector.

In the following example, after 2 seconds have passed, a .greeting will be inserted into the .container. Since we are listening to the insertion of this element, we can have a callback that triggers upon insertion.

const observe = (selector, callback, targetNode = document.body) => new MutationObserver(mutations => [...mutations] .flatMap((mutation) => [...mutation.addedNodes]) .filter((node) => node.matches && node.matches(selector)) .forEach(callback)) .observe(targetNode, { childList: true, subtree: true });
const createGreeting = () => { const el = document.createElement('DIV'); el.textContent = 'Hello World'; el.classList.add('greeting'); return el;
};
const container = document.querySelector('.container');
observe('.greeting', el => console.log('I have arrived!', el), container);
new Promise(res => setTimeout(() => res(createGreeting()), 2000)) .then(el => container.appendChild(el));
html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
body { display: flex; }
.container { display: flex; flex: 1; align-items: center; justify-content: center; }
.greeting { font-weight: bold; font-size: 2em; }
<div></div>

A solution returning a Promise and allowing to use a timeout (compatible IE 11+).

For a single element (type Element):

"use strict";
function waitUntilElementLoaded(selector) { var timeout = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var start = performance.now(); var now = 0; return new Promise(function (resolve, reject) { var interval = setInterval(function () { var element = document.querySelector(selector); if (element instanceof Element) { clearInterval(interval); resolve(); } now = performance.now(); if (now - start >= timeout) { reject("Could not find the element " + selector + " within " + timeout + " ms"); } }, 100); });
}

For multiple elements (type NodeList):

"use strict";
function waitUntilElementsLoaded(selector) { var timeout = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var start = performance.now(); var now = 0; return new Promise(function (resolve, reject) { var interval = setInterval(function () { var elements = document.querySelectorAll(selector); if (elements instanceof NodeList) { clearInterval(interval); resolve(elements); } now = performance.now(); if (now - start >= timeout) { reject("Could not find elements " + selector + " within " + timeout + " ms"); } }, 100); });
}

Examples:

waitUntilElementLoaded('#message', 800).then(function(element) { // element found and available element.innerHTML = '...';
}).catch(function() { // element not found within 800 milliseconds
});
waitUntilElementsLoaded('.message', 10000).then(function(elements) { for(const element of elements) { // .... }
}).catch(function(error) { // elements not found withing 10 seconds
});

Works for both a list of elements and a single element.

2

I usually use this snippet for Tag Manager:

<script>
(function exists() { if (!document.querySelector('<selector>')) { return setTimeout(exists); } // code when element exists
})();
</script>

I have developed an answer inspired by Jamie Hutber's.

It's a promise based function where you can set:

  • maximum number of tries - default 10;
  • delay in milliseconds - default 100 ms.

Therefore, by default, it will wait 1 second until the element appears on the DOM.

If it does not show up it will return a promise.reject with null so you can handle the error as per your wish.

Code

export function _waitForElement(selector, delay = 10, tries = 100) { const element = document.querySelector(selector); if (!window[`__${selector}`]) { window[`__${selector}`] = 0; window[`__${selector}__delay`] = delay; window[`__${selector}__tries`] = tries; } function _search() { return new Promise((resolve) => { window[`__${selector}`]++; setTimeout(resolve, window[`__${selector}__delay`]); }); } if (element === null) { if (window[`__${selector}`] >= window[`__${selector}__tries`]) { window[`__${selector}`] = 0; return Promise.resolve(null); } return _search().then(() => _waitForElement(selector)); } else { return Promise.resolve(element); }
}

Usage:

async function wait(){ try{ const $el = await waitForElement(".llama"); console.log($el); } catch(err){ console.error("Timeout - couldn't find element.") }
}
wait();

In the example above it will wait for the selector .llama. You can add a greater delay and test it here on the console of StackoverFlow.

Just add the class llama to any element on the DOM.

A cleaner example using MutationObserver:

new MutationObserver( mutation => { if (!mutation.addedNodes) return mutation.addedNodes.forEach( node => { // do stuff with node })
})
0

If you want it to stop looking after a while (timeout) then the following jQuery will work. It will time out after 10sec. I needed to use this code rather than pure JS because I needed to select an input via name and was having trouble implementing some of the other solutions.

 // Wait for element to exist. function imageLoaded(el, cb,time) { if ($(el).length) { // Element is now loaded. cb($(el)); var imageInput = $('input[name=product\\[image_location\\]]'); console.log(imageInput); } else if(time < 10000) { // Repeat every 500ms. setTimeout(function() { time = time+500; imageLoaded(el, cb, time) }, 500); } }; var time = 500; imageLoaded('input[name=product\\[image_location\\]]', function(el) { //do stuff here },time);

I try to avoid mutation observers if I can help it, so this is what I came up with. It looks similar to some of the other answers above. This function will look for the first element to exist within a given DOM call -- className being the expected usage but it can also accept tagName or Id. You could also add an argument for a precise index if you were looking for some number of elements with a given classname or tagname to have loaded.

 async function waitUntilElementExits(domkey,domquery,maxtime){ const delay = (ms) => new Promise(res => setTimeout(res, ms)); for(let i=0; i<maxtime; i=i+200){ await delay(200); let elm = document[domkey](domquery); if( (domkey == 'getElementById' && elm) || elm?.[0] ) break; } } // usage await waitUntilElementExits('getElementByClassName','some_class_name',10000)
2

Simple Javascript.

cont elementExist = setInterval(() => { var elm = document.getElementById("elementId") if (elm!=null) // call your function here to do something clearInterval(elementExist); }
}, 100);

Note: This will block other executions

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like