Initializing JavaScript - Old and Modern Methods

JavaScripts generally require an initialization routine to be run either onload or once the document has all the necessary components in place, in particular DOM elements required for the code. Traditionally, the onload event has been used. But this presents some problems:

  1. If the old-school approach is used, such as assigning a function to window.onload, a second script can overwrite a previous script's onload handler, which prevents the script from working.
  2. Initializing onload causes unnecessary delay. Scripts only need DOM elements and perhaps style sheets to be available but the load event isn't fired until all images and other resources have loaded.

This tutorial discusses both of these problems, but feel free to skip the old and jump down to information on the modern event model and about initializing when the DOM is ready

Onload the Old-Fashioned Way

Old-fashioned JavaScript might call a function onload by either including it in the body onload attribute or by assigning it to window onload.

<body onload="doSomething(argument)">

or:

window.onload = doSomething;

There can be problems combining code when using these approaches: competing onload handlers.

A novice may grab two scripts from an archive and place them in a page only to find that they do not work together. Perhaps one uses body onload and another window onload, or perhaps there are two separate assignments to window onload.

Sticking with old-fashioned solutions for the time being, you can include multiple function calls onload in the body tag by separating them with a semicolon:

<body onload="doSomething(argument); doMeToo()">

Or you can place multiple function calls inside an anonymous function assigned to window.onload as follows:

window.onload = function() { 
  fn1Name(args);
  fn2Name();
}

Although the above practices still work, they have fallen out of favor. The standard of unobtrusive JavaScript dictates that not only should JavaScript be removed from the document structure (i.e., not use HTML event handler attrubutes) but should also not obtrude on (have to commingle with) other code.

AddLoadEvent Functions

A better approach has been around and in broad use for quite some time: addLoadEvent functions, the original version most likely by Simon Willison.

function addLoadEvent(func) {
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      if (oldonload) {
        oldonload();
      }
      func();
    }
  }
}

You can call as many initialization routines as you like with addLoadEvent and they will all be successfully added to the stack. However, multiple window.onload function assignments placed ahead of the first call to addLoadEvent can't be collected, and window.onload function assignments placed after addLoadEvent function calls will overwrite its handling of window.onload up to that point.

On to more modern approaches to find a better solution.

addEventListener/attachEvent

The DOM Level 2 mechanism for attaching event handlers, addEventListener, (and the roughly equivalent attachEvent method used by Internet Explorer versions 5 through 8), allows multiple event handlers of the same type to be attached to the same object without interfering with each other. Scott Andrew LePera is credited for the original cross-browser addEvent function:

function addEvent(obj, evType, fn, useCapture){
  if (obj.addEventListener){
    obj.addEventListener(evType, fn, useCapture);
    return true;
  } else if (obj.attachEvent){
    var r = obj.attachEvent("on"+evType, fn);
    return r;
  }
}

The event file from dyn-web, dw_event.js, includes an add event function which can be used to call functions onload as follows:

dw_Event.add( window, 'load', init_dw_Scroll );
dw_Event.add( window, 'load', initRotator );

This approach appears to coexist amicably with window.onload and body onload. This is the method generally used in code examples at dyn-web.[1]

DOM Ready

Now to address the second problem posed above, that is, the unnecessary wait for all the document's resources to load when the code just needs to know when the DOM is ready.

The DOMContentLoaded event fires when the HTML has been parsed.[2] It is being standardized in HTML5 and is supported by all the up-to-date browsers, including Internet Explorer as of version 9.

Libraries like jQuery and Prototype provide DOM ready methods that include fallbacks for older browsers, like jQuery's ready method.

If you are not using one of these libraries you can find code to serve the same purpose. There are numerous versions. The one I prefer is from JavaScript the Definitive Guide 6th edition by David Flanagan:

var whenReady = (function() { // This function returns the whenReady() function
    var funcs = [];    // The functions to run when we get an event
    var ready = false; // Switches to true when the handler is triggered

    // The event handler invoked when the document becomes ready
    function handler(e) {
        // If we've already run once, just return
        if (ready) return;

        // If this was a readystatechange event where the state changed to
        // something other than "complete", then we're not ready yet
        if (e.type === "readystatechange" && document.readyState !== "complete")
            return;
        
        // Run all registered functions.
        // Note that we look up funcs.length each time, in case calling
        // one of these functions causes more functions to be registered.
        for(var i = 0; i < funcs.length; i++) 
            funcs[i].call(document);

        // Now set the ready flag to true and forget the functions
        ready = true;
        funcs = null;
    }

    // Register the handler for any event we might receive
    if (document.addEventListener) {
        document.addEventListener("DOMContentLoaded", handler, false);
        document.addEventListener("readystatechange", handler, false);
        window.addEventListener("load", handler, false);
    }
    else if (document.attachEvent) {
        document.attachEvent("onreadystatechange", handler);
        window.attachEvent("onload", handler);
    }

    // Return the whenReady function
    return function whenReady(f) {
        if (ready) f.call(document); // If already ready, just run it
        else funcs.push(f);          // Otherwise, queue it for later.
    }
}());

Back to top


  1. Some scripts may need images to finish loading or may need to refresh if initialized using a DOM ready function instead of onload. ^
  2. The firing of DOMContentLoaded may be deferred while style sheets are loaded under some circumstances. See DOMContentLoaded and stylesheets. ^