3
\$\begingroup\$

I need to create a JavaScript Promise that will not resolve until a specific HTML element is loaded on the page.

I don't have any way of knowing when this element will be loaded, other than to simply poll the DOM for it.

It seems there should be a more concise way to accomplish this task. I don't want anything that's "clever" to the point of being difficult to understand, but I'm open to any feedback on this pattern.

The code must be ES5 compatible for the most part. Polyfills are probably ok, but it has to run on IE10+.

function getContainer() {
    return new Promise(function (resolve, reject) {
        waitForContainerElement(resolve);
    });
}

function waitForContainerElement(resolve) {
    var $configElement = $("#container");
    if ($configElement.length === 0) {
        setTimeout(waitForContainerElement.bind(this, resolve), 30);
    } else {
        resolve($configElement);
    }
}

Usage:

getContainer.then(function($container){
    ...
});

I would normally implement a max poll time, but didn't want that to cloud the issue here.

\$\endgroup\$
  • \$\begingroup\$ Is there a reason why you can't wait for the user to start interacting with the element before doing something? Most events bubble up through the DOM. Is there a click or focusin event that you can utilize? Can you elaborate on why you need to wait for the element, and what you will be doing with it? \$\endgroup\$ – Greg Burghardt May 27 '15 at 15:52
  • \$\begingroup\$ No, there's no user interaction. There's a lot of detail that I simplified for this discussion. In this case, the element is used to specify meta-data that is used by other script on the page. (it's actually a custom HTML element, not a container div). I suppose I could use mutation events to watch for DOM changes. \$\endgroup\$ – Joseph Gabriel May 27 '15 at 16:04
  • \$\begingroup\$ How does this element magically appear? Does it happen via AJAX? Page load? \$\endgroup\$ – Greg Burghardt May 27 '15 at 16:05
  • \$\begingroup\$ This element is usually already on the initial page, but there is a bit of a race condition where the startup code sometimes runs before the element is loaded in the browser. Putting the script after the element would solve the issue, but in this environment, I can't guarantee that the order will be correct. As a result, I am implementing this pattern as a more robust way to ensure that I can find the element before the code that needs it is executed. \$\endgroup\$ – Joseph Gabriel May 27 '15 at 16:18
  • 2
    \$\begingroup\$ @JosephGabriel Any reason you're not waiting on the usual document-ready event? Sure, you only need a special meta tag, but waiting for DOM ready is bullet-proof \$\endgroup\$ – Flambino May 27 '15 at 16:35
4
\$\begingroup\$

One idea would be to put the script tags at the end of the document's body - not in the head at all. It's a common trick in itself, often used to speed up load time, because it means no time is spent parsing scripts before moving on to the page's actual content. It also means that the scripts will have everything that's "before" them in the DOM ready for use.

Alternatively: If you're unwilling to move all the scripts, but still willing to simply wait for the document to report ready, there's no need for polling. See MDN's documentation for more. You may want to check document.readyState first, since attaching an event listener too late (after the document has loaded), is pointless: The event only fires once.

But wait! It appears you're already using jQuery, in which case all you have to do is:

$(function () {
  // code to run on document ready
});

That code can resolve a promise, but usually you'd just put that in your various scripts to make sure they only run once the document's ready (e.g. $(initWidget); where initWidget is whatever function kicks something off). It's how most jQuery scripts are written.

\$\endgroup\$
  • \$\begingroup\$ I like this pattern jQuery(function($) { now you use the jQuery variable in global scope and get a local variable ($) pointing to jQuery. \$\endgroup\$ – andlrc May 28 '15 at 8:36

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.