Simon Willison’s Weblog

Executing JavaScript on page load

Peter-Paul Koch recently wrote:

In my opinion, recent advances in JavaScript theory call for the removal of the event handlers that some Web developers-and all WYSIWYG editors-deploy in large masses in their XHTML files, where they don’t belong.

PPK is talking about inline event attributes such as the infamous onclick="" and onmouseover="" which have infested our HTML ever since Netscape introduced JavaScript back in version 2.0 of their browser. The alternative to these handlers is to add event handlers to elements after the document has loaded. PPK has detailed coverage of the various ways of doing this on his QuirksMode site.

In my work with unobtrusive JavaScript, I’ve found that by far the most common action I take is “registering” a script to be executed once the page has finished loading. There are a number of ways of doing this, which I described in my article Enhancing Structural Markup with JavaScript. Unfortunately, none of them are perfect if you wish to write truly reusable scripts.

For a script (such as my blockquote citations script discussed in the article) to be properly reusable, it needs to behave nicely in the presence of other scripts. This means that assigning a callback function directly to the window.onload handler is out of the question as doing so will over-ride previously assigned callbacks from other scripts. The correct way of adding a handler to an event without over-riding other handlers is to use modern event attachment method, which sadly differ between IE/Windows and other browsers. Scott Andrew’s addEvent function handles the differences for you but comes with one major and rarely discussed drawback: it fails silently in IE5/Mac. If you care about the many Mac users still on OS9, you need to support that browser.

Anyway, I believe I’ve found a solution. Check this out:


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

addLoadEvent(nameOfSomeFunctionToRunOnPageLoad);
addLoadEvent(function() {
  /* more code to run on page load */ 
});

The addLoadEvent function takes as an argument another function which should be executed once the page has loaded. Unlike assigning directly to window.onload, the function adds the event in such a way that any previously added onload functions will be executed first.

The way this works is relatively simple: if window.onload has not already been assigned a function, the function passed to addLoadEvent is simply assigned to window.onload. If window.onload has already been set, a brand new function is created which first calls the original onload handler, then calls the new handler afterwards.

addLoadEvent has one very important property: it will work even if something has previously been assigned to window.onload without using addLoadEvent itself. This makes it ideal for use in scripts that may be executing along side other scripts that have already been registered to execute once the page has loaded.

I’ve tested the above code successfully on IE 5, 5.5 and 6 for Windows; IE 5 and Safari for Mac; Opera 7.5 and FireFox on Mac (which should mean it works with those browsers on Windows as well). Opera 6 for Mac failed the test but has poor JavaScript support anyway and is hopefully becoming more and more rare now that Opera 7 has matured.

I’ve created a test page for the function. I’d be interested to here any bug reports from browsers I haven’t covered.

I’m still considering ways in which this technique could be extended to work for generic events rather than just page loads. The challenge there would be to ensure that information about the event itself was passed to the event handlers in a consistent manner. For page load events this isn’t an issue as the event object does not contain any valuable information.

Update: I’ve written the new technique up on my SitePoint blog and incorporated an explanation of closures and how they are used to preserve any previously assigned onload handlers.

Update 28th May 2006: Billy Pan pointed out that the original code caused a runtime error in IE 7. His fix was to add an if (oldonload); I have rolled this fix in to the code shown above.

This is Executing JavaScript on page load by Simon Willison, posted on 26th May 2004.

Tagged , , ,

Next: Time to fix those broken pages

Previous: TBL on TLDs

Previously hosted at http://simon.incutio.com/archive/2004/05/26/addLoadEvent