I have a property of an element that is injected into my page from a 3rd party:
document.querySelector('#embed-container #mf2-events').jsMF2
the jsMF2 is said injected property. I need to wait until the jsMF2 property is defined in order to do work with it. Originally, I just set a timeout, but this is undesirable for many reasons. Is there a way I can spin until the property or set a callback? If this was a C program, I would write something like this:
while (document.querySelector('#embed-container #mf2-events').jsMF2 === undefined ||
document.querySelector('#embed-container #mf2-events').jsMF2 === null) { }
// do work
To wait to do something until an element exists in the DOM, you can use JavaScript MutatationObserver API. As the name suggests, it can be used to listen out for changes in the DOM. You can then fire a (callback) function in response to this.
If you need to wait for an element to appear, the async wait utilities allow you to wait for an assertion to be satisfied before proceeding. The wait utilities retry until the query passes or times out. The async methods return a Promise, so you must always use await or .then(done) when calling them.
You don't want a busy-wait, because then no other JavaScript can run (not to mention large parts of the browser UI), and so the property won't get defined.
Ideally, whatever's providing that property would have an event it fires that you can hook into. I'm going to assume that you've looked and not found one. :-)
Once support for the latest stuff in ECMAScript6 (aka "ES6") becomes widespread (it isn't at present), you might be able to use Proxy
for this (provided your target browsers allowed Proxy
on their HTML element instances). But sufficiently widespread support for Proxy
will take a couple of years if not longer (and Proxy
can't be shimmed/polyfilled). (In ES7, you could use Object.observe
, but presumably Proxy
, which is defined by the current [as of June 2015] standard, will be broadly-supported before an ES7 technology is.)
Until/unless you can use Proxy
, a timer is indeed the correct way to handle this situation. It can be a really aggressive timer if necessary.
If the element is known to exist but you're waiting for the property:
check(function(element) {
// It's there now, use it
// x = element.jsMF2
});
function check(callback) {
var element = document.querySelector('#embed-container #mf2-events');
if (element && 'jsMF2' in element) {
setTimeout(callback.bind(null, element), 0);
} else {
setTimeout(check.bind(null, callback), 0);
}
}
Most browsers will fire that timer immediately when the JavaScript thread is available the first couple of times, then throttle it back to at least a 4ms delay for subsequent calls. Still pretty fast.
You don't have to be hyper-aggressive, though; humans are slow compared to computers, you could probably use 10, 20, or even 50ms.
If there's any chance that the property won't appear, you want to stop that repeated series of setTimeout
eventually (after a second, after 10 seconds, after 30 seconds, after 60 seconds, whatever's appropriate to your use case). You can do that by remembering when you started, and then simply giving up rather than rescheduling if it's been too long:
var started = Date.now();
check(function(element) {
// It's there now, use it
// x = element.jsMF2
});
function check(callback) {
var element = document.querySelector('#embed-container #mf2-events');
if (element && 'jsMF2' in element) {
setTimeout(callback.bind(null, element), 0);
} else {
if (Date.now() - started > 1000) { // 1000ms = one second
// Fail with message
} else {
setTimeout(check.bind(null, callback), 0);
}
}
}
Side note: The query
var document.querySelector('#embed-container #mf2-events');
...is a bit odd. It says: Give me the first element with the id
mf2-events
found inside an element with the id
embed-container
. But id
values must be unique on the page. So all that really says is "Get tme the #mfs-events
element, but only if it's inside a #embed-container
element."
Unless that's really what you meant, the dramatically faster
var document.getElementById('mf2-events');
...would be the way to go.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With