最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript: waiting until property of an element is ready to continue - Stack Overflow

programmeradmin4浏览0评论

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

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
Share Improve this question edited Jul 31, 2015 at 19:05 user3689167 asked Jul 31, 2015 at 16:03 user3689167user3689167 8731 gold badge14 silver badges29 bronze badges 3
  • You mean in a multi-threaded C program that's what you'd do. JavaScript in browsers (other than when used as web workers) is indeed run on a single thread. – T.J. Crowder Commented Jul 31, 2015 at 16:05
  • You will either need to poll with setTimeout() or you can use a MutationObserver in a modern browser. Spinning in a while loop will just lock up the browser and it will never succeed because no other code can run to actually cause the change to occur. – jfriend00 Commented Jul 31, 2015 at 16:08
  • @user3689167: Right, but if the program were truly single-threaded, it would busy-wait forever -- because nothing ever gets a chance to set jsMF2. To do that while busy-waiting, you need at least two threads. – T.J. Crowder Commented Jul 31, 2015 at 16:14
Add a ment  | 

3 Answers 3

Reset to default 4

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") bees 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 pared to puters, 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.

The only real way out here is to use a callback. How is the property set? If it is set by, say, ajax request, you need to add a function as the request parameter that will be run when the request is satisfied. The topics you will probably need are callbacks, asynchronous JavaScript and maybe promises, which are modern approach to async calla.

There were DOM Events which is not very popular and later replace it with a new standard MutationObserver.

But you should check if your target browser support it or implement both.


EDIT: You may also use Object.observe() if the property is not an dom attribute.

发布评论

评论列表(0)

  1. 暂无评论