89

The question is so like a zillion others here and on the web - How to check if DOM has loaded in Javascript? But here's the catch:

  • Without using a framework like jQuery etc;
  • Without knowing if your script has been loaded via a statically placed <script> tag or via some other Javascript much later after the DOM has already loaded.

Can this be done more or less reliably and with cross-browser compatibility?

Added: Let me clarify: I'm writing a standalone .JS file which can be included in arbitrary webpages. I want to execute code AFTER the DOM has been loaded. But I don't know HOW my script will be included. It could be by placing a <script> tag (in which case the traditional onload or DOM-readiness solutions will work); or it could be loaded via AJAX or some other means, much later after the DOM is already loaded (so the previously mentioned solutions will never fire).

Vilx-
  • 104,512
  • 87
  • 279
  • 422
  • 3
    The [`document.readyState`](https://developer.mozilla.org/en/DOM/document.readyState) property can be used, although I'm not sure about browser compatibility... – Digital Plane Nov 11 '11 at 22:19
  • 1
    Not very good, I'm afraid. :( – Vilx- Nov 11 '11 at 22:20
  • 5
    **[Have](http://www.dustindiaz.com/smallest-domready-ever) [you](https://gist.github.com/842085) [tried](https://gist.github.com/841930) [to](http://blog.shawndumas.com/cross-browser-domready) [search](http://stackoverflow.com/questions/1206937/javascript-domready) [?](http://stackoverflow.com/questions/2732171/javascript-dom-ready-without-an-entire-framework)** – topek Nov 11 '11 at 22:27
  • 1
    See this small utility: http://github.com/cms/domready it's about ~0.5kb minified. – Christian C. Salvadó Nov 11 '11 at 22:29
  • 1
    @DigitalPlane - I take it back. It has damn good browser support! Add it as an answer and I'll accept it. – Vilx- Nov 11 '11 at 22:43
  • 1
    Then I'll retract the accepted answer and award it to someone else. :) – Vilx- Nov 12 '11 at 12:07

7 Answers7

110

The document.readyState property can be used to check if the document is ready. From MDN:

Values

The readyState of a document can be one of following:

  • loading – The document is still loading.
  • interactive – The document has finished loading and the document has been parsed but sub-resources such as images, stylesheets and frames are still loading.
  • complete – The document and all sub-resources have finished loading. The state indicates that the load event is about to fire.

Code example:

if(document.readyState === "complete") {
    // Fully loaded!
}
else if(document.readyState === "interactive") {
    // DOM ready! Images, frames, and other subresources are still downloading.
}
else {
    // Loading still in progress.
    // To wait for it to complete, add "DOMContentLoaded" or "load" listeners.

    window.addEventListener("DOMContentLoaded", () => {
        // DOM ready! Images, frames, and other subresources are still downloading.
    });

    window.addEventListener("load", () => {
        // Fully loaded!
    });
}
MultiplyByZer0
  • 6,302
  • 3
  • 32
  • 48
Digital Plane
  • 37,354
  • 7
  • 57
  • 59
  • 4
    keep in mind that `addEventListener()` was added with IE9, which is not available for Windows XP – Christoph Nov 11 '11 at 23:33
  • 1
    Of course. This goes together with the other solutions for a DOMReady event simulation. – Vilx- Nov 12 '11 at 12:08
  • 7
    for one, this isn't cross browser effective. two, this waits for images to load as well. this acts like window.onload. – Brian Sweat Jun 12 '13 at 18:49
  • 5
    Shouldn't you listen for an event named `load` rather than `onload`? – natevw Apr 07 '14 at 22:05
  • 2
    [Esoterica warning] * I believe it would be more appropriate to use document.readyState == "complete" rather than document.readyState === "complete" * Depending on what your code will be doing, it might be more appropriate to wait until all the images have loaded * Isn't it depressing that the browser makers are all so uncompromisingly incompetent? Everything about the design of JavaScript is definitively wrong. – debater Jun 03 '16 at 15:18
  • The question asked how to detect if the DOM has been loaded, so this should use the `DOMContentLoaded` event. – Evan Byrne Jan 26 '18 at 19:00
  • document.addEventListener("DOMContentLoaded", function () {/* code */}, false) this worked for me. Thanks! – JRichardsz May 21 '19 at 17:21
10

Firefox, Opera and Webkit-based browsers have a document-level event DOMContentLoaded that you can listen for with document.addEventListener("DOMContentLoaded", fn, false).

It is more complicated in IE. What jQuery does in IE is watch onreadystatechange on the document object for a particular readystate with a backup of the document.onload event. document.onload fires later than the DOM is ready (only when all images have finished loading) so it's only used as a backstop in case the earlier events don't work for some reason.

If you spend some time Googling, you will find code to do this. I figure the most vetted code to do this is in the large frameworks like jQuery and YUI so, even if I'm not using that framework, I look in their source code for techniques.

Here's the main part of jQuery 1.6.2 source for document.ready():

bindReady: function() {
    if ( readyList ) {
        return;
    }

    readyList = jQuery._Deferred();

    // Catch cases where $(document).ready() is called after the
    // browser event has already occurred.
    if ( document.readyState === "complete" ) {
        // Handle it asynchronously to allow scripts the opportunity to delay ready
        return setTimeout( jQuery.ready, 1 );
    }

    // Mozilla, Opera and webkit nightlies currently support this event
    if ( document.addEventListener ) {
        // Use the handy event callback
        document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );

        // A fallback to window.onload, that will always work
        window.addEventListener( "load", jQuery.ready, false );

    // If IE event model is used
    } else if ( document.attachEvent ) {
        // ensure firing before onload,
        // maybe late but safe also for iframes
        document.attachEvent( "onreadystatechange", DOMContentLoaded );

        // A fallback to window.onload, that will always work
        window.attachEvent( "onload", jQuery.ready );

        // If IE and not a frame
        // continually check to see if the document is ready
        var toplevel = false;

        try {
            toplevel = window.frameElement == null;
        } catch(e) {}

        if ( document.documentElement.doScroll && toplevel ) {
            doScrollCheck();
        }
    }
},
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • 1
    Once again - this will not work if I attach my event handler after the DOM has already loaded. – Vilx- Nov 11 '11 at 22:22
  • 4
    Look in the jQuery source I've included. That's why they have the line that starts with `if ( document.readyState === "complete" ) {` so they will handle the case where the document is already ready. If you're building your own mini-framework to do this, you have to build in this type of functionality yourself. – jfriend00 Nov 11 '11 at 22:25
10

If relying on document.readyState is ok, quick-and-dirty solution with polling:

(function() {
    var state = document.readyState;
    if(state === 'interactive' || state === 'complete') {
        // do stuff
    }
    else setTimeout(arguments.callee, 100);
})();
Christoph
  • 164,997
  • 36
  • 182
  • 240
  • `document.readyState` is the answer to my problems. As I've stated several times, attaching to a "DOMReady"-kind of event is not a problem. The problem is when the readyState==complete, so the DOMReady event will never trigger. – Vilx- Nov 12 '11 at 12:13
  • @Vilx-: my point is that if you're relying on `document.readyState` anyway (ie you don't want to support legacy non-IE browsers), there's no need for any browser-specific code at all (`DOMContentLoaded` vs. `onload` vs. `onreadystatechange`, `addEventListener` vs `attachEvent`) - the above solution works everywhere – Christoph Nov 12 '11 at 12:20
  • As far as I can see, the only browser I won't support is FireFox below 3.6. And, to be honest, it is an edge case (mostly the script will be included the traditional way), so if I miss it for some very rare combination, it won't be that much of a big deal. :P – Vilx- Nov 12 '11 at 12:22
9

This works for all browsers and is short and concise:

var execute = function () {
  alert("executing code");  
};

if ( !!(window.addEventListener) )
  window.addEventListener("DOMContentLoaded", execute)
else // MSIE
  window.attachEvent("onload", execute)
rogerdpack
  • 62,887
  • 36
  • 269
  • 388
John Foley
  • 4,373
  • 3
  • 21
  • 23
7

The DOMContentLoaded event is fired when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading.Good thing is chrome, firefox, IE9, opera and safari supports it equally

document.addEventListener("DOMContentLoaded", function(event) 
{
    console.log("DOM fully loaded and parsed");
}

NOTE : Internet Explorer 8 supports the readystatechange event, which can be used to detect when the DOM is ready.

Malik Khalil
  • 6,454
  • 2
  • 39
  • 33
3

Here is one way by running script at the bottom of the page. In addition by using the window.onload you can wait for all images/scripts to be loaded. Or you could simply place code at the bottom not waiting for images to be loaded.

<html>
<head>
</head>
<body>
</body>
<script language="text/javascript">
  window.onload = (function (oldOnLoad) {
    return function () {
      if (oldOnLoad) { 
        olOnLoad();  //fire old Onload event that was attached if any.
      }
      // your code to run after images/scripts are loaded
    }
  })(window.onload);

  // your code to run after DOM is loaded
</script>
</html>

Edited: for Vilx's Comment

Many onload bindings here is an example http://jsfiddle.net/uTF2N/3/

John Hartsock
  • 85,422
  • 23
  • 131
  • 146
  • +1. You might want to add an explanation of how this works for people searching later and finding this that might not have a lot of experience, though. – Ken White Nov 11 '11 at 22:16
  • 1
    I think you violate: "Without knowing if your script has been loaded via a statically placed – Levi Morrison Nov 11 '11 at 22:16
  • I'm sorry, I hadn't said I was talking about a standalone script. I clarified. – Vilx- Nov 11 '11 at 22:16
  • @Levi Morrison. window.onload runs after all images/scripts have been loaded. – John Hartsock Nov 11 '11 at 22:18
  • In my case I wouldn't even care so much, but even so there are plenty of DOM-readiness scripts available, so it's of no concern. The main problem is that I don't know if maybe the onload event has already fired long ago, so attaching another handler to it will have no effect. – Vilx- Nov 11 '11 at 22:19
  • @Vilx yeah I was trying to account for that in my closure but I forgot to put the call to the oldOnload Event. but basically you can reuse this method to bind many onloads. – John Hartsock Nov 11 '11 at 22:22
  • No, no, no, you don't understand! What if `onload` had already fired 5 minutes ago, and only then my script comes along and tries this method? It's code will never get executed! – Vilx- Nov 11 '11 at 22:24
  • @JohnHartsock, note that you're immediately executing the anonymous function, and assigning its result to `window.onload`... You need to return a function, that will be the handler assigned to `window.onload` otherwise you will assign `undefined`, since the function has no return value, and it will be executed when the DOM is loaded, not on the `onload` event. – Christian C. Salvadó Nov 11 '11 at 22:24
1

We can just use DOMContentLoaded is Pure JavaScript approach.

const domReady = (callBack) => {
  document.addEventListener('DOMContentLoaded', callBack);
  if( document.readyState === "interactive" || document.readyState === "complete" ) {
    callBack();
  }
}

const WhenDomLoaded = () => {
  console.log("Dom Loaded now!")
}

domReady(() => WhenDomLoaded() );
GMKHussain
  • 3,342
  • 1
  • 21
  • 19