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 t
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="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button>New content</button>
<div id="newContent"></div>
Note: Spanish Mozilla docs about
MutationObserver
are more detailed if you want more information.
if you have async dom changes, this function checks (with time limit in seconds) for the DOM elements, it will not be heavy for the DOM and its Promise based :)
function getElement(selector, i = 5) {
return new Promise(async (resolve, reject) => {
if(i <= 0) return reject(`${selector} not found`);
const elements = document.querySelectorAll(selector);
if(elements.length) return resolve(elements);
return setTimeout(async () => await getElement(selector, i-1), 1000);
})
}
// Now call it with your selector
try {
element = await getElement('.woohoo');
} catch(e) { // catch the e }
//OR
getElement('.woohoo', 5)
.then(element => { // do somthing with the elements })
.catch(e => { // catch the error });
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);
}
}
});
}
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);
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.
A cleaner example using MutationObserver:
new MutationObserver( mutation => {
if (!mutation.addedNodes) return
mutation.addedNodes.forEach( node => {
// do stuff with node
})
})