Javascript domready?

Viewed 101775

I know I can use different frameworks like prototype or jQuery to attach a function to the window.onload, but's not what I'm looking for.

I need something like .readyState so that I can do something like this:

if(document.isReady){
  var id = document.getElem ...
}

is there any other way than using what the frameworks do?

11 Answers

While I usually advocate avoiding using frameworks unless necessary, I'd say using one in this case is perfectly fine. Here's jQuery:

$(function () {
    // do stuff after DOM has loaded
});

Note that it is NOT the same as an window.onload event, since onload executes first after other resources have been loaded (images etc.) The code I used in my example will execute when the DOM has finished loading, i.e., when the full HTML structure is available (not necessarily when images, CSS, etc. is available.)

If you want a checkable variable, you can set one in the ready-function:

var documentIsReady = false;
$(function () { documentIsReady = true; });

Of course you can find even more light-weight libraries than jQuery if all you want to do is to check for DOM-ready. But use a library in cases where different browsers behave very differently (this is one such case.)

Using some code from the DOMAssistant library, making your own "DOM is ready" function shouldn't be too hard:

var domLoaded = function (callback) {
    /* Internet Explorer */
    /*@cc_on
    @if (@_win32 || @_win64)
        document.write('<script id="ieScriptLoad" defer src="//:"><\/script>');
        document.getElementById('ieScriptLoad').onreadystatechange = function() {
            if (this.readyState == 'complete') {
                callback();
            }
        };
    @end @*/
    /* Mozilla, Chrome, Opera */
    if (document.addEventListener) {
        document.addEventListener('DOMContentLoaded', callback, false);
    }
    /* Safari, iCab, Konqueror */
    if (/KHTML|WebKit|iCab/i.test(navigator.userAgent)) {
        var DOMLoadTimer = setInterval(function () {
            if (/loaded|complete/i.test(document.readyState)) {
                callback();
                clearInterval(DOMLoadTimer);
            }
        }, 10);
    }
    /* Other web browsers */
    window.onload = callback;
};

Not tested, but it should work. I simplified it from DOMAssistant, because DOMAssistant allows multiple callbacks and has checking to make sure you can't add the same function twice etc.

jQuery doesn't use window.onload.

$(document).ready() waits until the DOM has loaded and can be traversed (the rest of the content may or may not be loaded by that point).

If you pull up the source for jQuery and sort through the mess, you'll find the work is done by the bindReady() method which has several different implementations for different browsers and only when all of those implementations fail does it fall back on listening for the load event for the window.

This is actually one of the biggest reasons most people use frameworks like jQuery because the solution to this is not consistent across browsers.

It seems this question was asked a reallly long time ago. Today we now have document.readyState, and even an event that goes with it.

To achieve the equivalent of:

if(document.isReady){
  var id = document.getElem ...
}

Use the following:

const doTask = () => {
  var id = document.getElem ....
}

if (document.readyState !== 'loading') {
  // in case we missed the `loading` state, start running the
  // task now...
  doTask();
} else {
  //  otherwise register a listener to be notified of the next state
  document.addEventListener('readystatechange', event => {
    if (event.target.readyState === 'interactive') {
      doTask();
    }
  });
}

The other answers which use window:DOMContentLoaded event, will receive that event after the above event listener runs, which means that using the readystatechange event allows you to start working on the DOM before DOMContentLoaded is fired, while still being able to interact with DOM elements.

The MDN docs provide an example here. Just in case the example goes away, here it is:

const log = document.querySelector('.event-log-contents');
const reload = document.querySelector('#reload');

reload.addEventListener('click', () => {
  log.textContent ='';
  window.setTimeout(() => {
      window.location.reload(true);
  }, 200);
});

window.addEventListener('load', (event) => {
    log.textContent = log.textContent + 'load\n';
});

document.addEventListener('readystatechange', (event) => {
    log.textContent = log.textContent + `readystate: ${document.readyState}\n`;
});

document.addEventListener('DOMContentLoaded', (event) => {
    log.textContent = log.textContent + `DOMContentLoaded\n`;
});
<div class="controls">
  <button id="reload" type="button">Reload</button>
</div>

<div class="event-log">
  <label>Event log:</label>
  <textarea readonly class="event-log-contents" rows="8" cols="30"></textarea>
</div>

Related