170

I want to call a function after a document loads, but the document may or may not have finished loading yet. If it did load, then I can just call the function. If it did NOT load, then I can attach an event listener. I can't add an eventlistener after onload has already fired since it won't get called. So how can I check if the document has loaded? I tried the code below but it doesn't entirely work. Any ideas?

var body = document.getElementsByTagName('BODY')[0];
// CONDITION DOES NOT WORK
if (body && body.readyState == 'loaded') {
    DoStuffFunction();
} else {
    // CODE BELOW WORKS
    if (window.addEventListener) {  
        window.addEventListener('load', DoStuffFunction, false);
    } else {
        window.attachEvent('onload', DoStuffFunction);
    }
}
laurent
  • 88,262
  • 77
  • 290
  • 428
Marcos
  • 1,709
  • 2
  • 11
  • 3
  • 5
    "I want to call a function after a document loads, but the document may or may not have finished loading yet." This does not compute. – Richard Simões Jun 11 '09 at 00:18
  • 7
    I believe he means he doesn't have control over when his block of code is initially run, but wants to ensure DoStuffFunction() isn't called before the document has finished loading. – Ben Blank Jun 11 '09 at 00:20

9 Answers9

268

There's no need for all the code mentioned by galambalazs. The cross-browser way to do it in pure JavaScript is simply to test document.readyState:

if (document.readyState === "complete") { init(); }

This is also how jQuery does it.

Depending on where the JavaScript is loaded, this can be done inside an interval:

var readyStateCheckInterval = setInterval(function() {
    if (document.readyState === "complete") {
        clearInterval(readyStateCheckInterval);
        init();
    }
}, 10);

In fact, document.readyState can have three states:

Returns "loading" while the document is loading, "interactive" once it is finished parsing but still loading sub-resources, and "complete" once it has loaded. -- document.readyState at Mozilla Developer Network

So if you only need the DOM to be ready, check for document.readyState === "interactive". If you need the whole page to be ready, including images, check for document.readyState === "complete".

laurent
  • 88,262
  • 77
  • 290
  • 428
  • 3
    @costa, if `document.readyState == "complete"`, it means everything, including images, have been loaded. – laurent Mar 30 '13 at 17:23
  • I'm using combination of both events and `document.readyState` to make sure the function starts after DOM has been parsed, or later, depending on when it has been called. Is there an event for *interactive* `readyState`? – Tomáš Zato Nov 24 '13 at 20:46
  • 3
    I'd use ```/loaded|complete/.test(document.readyState)```. Some browsers set ```document.readyState``` to "loaded" instead of "complete". Also, document.readyState does NOT ensure fonts have been loaded, there's no real good way to test for that without a plugin. – Ryan Taylor May 23 '14 at 19:39
  • 2
    What about `document.onreadystatechange`? This seems more streamlined if browsers support it. http://stackoverflow.com/questions/807878/javascript-that-executes-after-page-load/25786528#25786528 –  Jan 12 '15 at 15:30
  • I think this results in an infinite loop if `init()` results in an error. @galambalazs [answer takes care of that case](http://stackoverflow.com/a/3144510/980524) – wpp Jan 20 '15 at 11:18
  • @wpp, you are right. To be safe, it's better to clear the interval before calling `init()`. I've updated the code. – laurent Jan 20 '15 at 13:51
  • I think to avoid setInterval, follow the answer below. – cantfindaname88 Jun 01 '15 at 17:11
  • 2
    I think it would be better to check for `document.readyState !== "loading"`, for the "DOM ready" state. This prevents issues cropping up if the `readyState` is checked late for some reason (after the "interactive" state). – rnevius Sep 21 '15 at 23:20
32

No need for a library. jQuery used this script for a while, btw.

http://dean.edwards.name/weblog/2006/06/again/

// Dean Edwards/Matthias Miller/John Resig

function init() {
  // quit if this function has already been called
  if (arguments.callee.done) return;

  // flag this function so we don't do the same thing twice
  arguments.callee.done = true;

  // kill the timer
  if (_timer) clearInterval(_timer);

  // do stuff
};

/* for Mozilla/Opera9 */
if (document.addEventListener) {
  document.addEventListener("DOMContentLoaded", init, false);
}

/* for Internet Explorer */
/*@cc_on @*/
/*@if (@_win32)
  document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
  var script = document.getElementById("__ie_onload");
  script.onreadystatechange = function() {
    if (this.readyState == "complete") {
      init(); // call the onload handler
    }
  };
/*@end @*/

/* for Safari */
if (/WebKit/i.test(navigator.userAgent)) { // sniff
  var _timer = setInterval(function() {
    if (/loaded|complete/.test(document.readyState)) {
      init(); // call the onload handler
    }
  }, 10);
}

/* for other browsers */
window.onload = init;
gblazex
  • 49,155
  • 12
  • 98
  • 91
  • 3
    Totally missed the point of the question. `init` will not run if this code you posted is included after the page has already loaded, which is the point of the OP: he/she can't control when his script is included. (note that only the `setInterval` branch will work) – Roatin Marth Jan 24 '12 at 16:35
  • 3
    This belongs in a library. Along with all the other wheels people commonly re-invent. – b01 May 13 '13 at 22:52
14

You probably want to use something like jQuery, which makes JS programming easier.

Something like:

$(document).ready(function(){
   // Your code here
});

Would seem to do what you are after.

dan
  • 552
  • 2
  • 6
9
if(document.readyState === 'complete') {
    DoStuffFunction();
} else {
    if (window.addEventListener) {  
        window.addEventListener('load', DoStuffFunction, false);
    } else {
        window.attachEvent('onload', DoStuffFunction);
    }
}
Jman
  • 928
  • 7
  • 12
7

If you actually want this code to run at load, not at domready (ie you need the images to be loaded as well), then unfortunately the ready function doesn't do it for you. I generally just do something like this:

Include in document javascript (ie always called before onload fired):

var pageisloaded=0;
window.addEvent('load',function(){
 pageisloaded=1;
});

Then your code:

if (pageisloaded) {
 DoStuffFunction();
} else {
 window.addEvent('load',DoStuffFunction);
}

(Or the equivalent in your framework of preference.) I use this code to do precaching of javascript and images for future pages. Since the stuff I'm getting isn't used for this page at all, I don't want it to take precedence over the speedy download of images.

There may be a better way, but I've yet to find it.

Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
Nathan Stretch
  • 1,028
  • 1
  • 11
  • 23
4

The above one with JQuery is the easiest and mostly used way. However you can use pure javascript but try to define this script in the head so that it is read at the beginning. What you are looking for is window.onload event.

Below is a simple script that I created to run a counter. The counter then stops after 10 iterations

window.onload=function()
{
    var counter = 0;
    var interval1 = setInterval(function()
    { 
        document.getElementById("div1").textContent=counter;
        counter++; 
        if(counter==10)
        {
            clearInterval(interval1);
        }
    },1000);

}
Math
  • 3,334
  • 4
  • 36
  • 51
Talha
  • 1,546
  • 17
  • 15
4

Mozila Firefox says that onreadystatechange is an alternative to DOMContentLoaded.

// alternative to DOMContentLoaded
document.onreadystatechange = function () {
    if (document.readyState == "complete") {
        initApplication();
    }
}

In DOMContentLoaded the Mozila's doc says:

The DOMContentLoaded event is fired when the document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading (the load event can be used to detect a fully-loaded page).

I think load event should be used for a full document+resources loading.

Mostafa Talebi
  • 8,825
  • 16
  • 61
  • 105
2

Try this:

var body = document.getElementsByTagName('BODY')[0];
// CONDITION DOES NOT WORK
if ((body && body.readyState == 'loaded') || (body &&  body.readyState == 'complete') ) {
    DoStuffFunction();
} else {
    // CODE BELOW WORKS
    if (window.addEventListener) {
        window.addEventListener('load', DoStuffFunction, false);
    } else {
        window.attachEvent('onload',DoStuffFunction);
    }
}
László Papp
  • 51,870
  • 39
  • 111
  • 135
smit
  • 21
  • 1
1

I have other solution, my application need to be started when new object of MyApp is created, so it looks like:

function MyApp(objId){
     this.init=function(){
        //.........
     }
     this.run=function(){
          if(!document || !document.body || !window[objId]){
              window.setTimeout(objId+".run();",100);
              return;
          }
          this.init();
     };
     this.run();
}
//and i am starting it 
var app=new MyApp('app');

it is working on all browsers, that i know.

vitus
  • 91
  • 1
  • 1
  • 4