Feed Sign in with OpenID OpenID

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 , , ,

View blog reactions

Next: Time to fix those broken pages

Previous: TBL on TLDs

61 comments

  1. If you only have one or two functions, a quicker means of assigning multiple events is:

    var oldEvt = window.onload; window.onload = function() { if (oldEvt) oldEvt(); doOtherStuffHere(); }

    Just create a reference to the older function and test for it within the new function; that's compact and works everywhere.

    In the meantime, I've written another event manager function that is ridiculously complicated; as well as swapping between addEventListener and onevent = function(){} depending on browser support, it can collate and manage multiple true/false return values and capture/route document events for the old NS4 event model properly. If that sounds like it interests you, head over to my site and get the "DHTML Layer API" listed under "DHTML", v1.1 is recommended. </selfpromotion>.

    Angus Turnbull - 26th May 2004 08:02 - #

  2. 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.

    What about this:

    function addEvent(oTarget, sType, fpDest) {
      var oOldEvent = oTarget[sType];
      if (typeof oOldEvent != "function") {
        oTarget[sType] = fpDest;
      } else {
        oTarget[sType] = function(e) {
          oOldEvent(e);
          fpDest(e);
        }
      }
    }

    Passing the event argument should make the function work for all events while respecting the event object. In IE the event object is global, and there can only be one event object per event (which makes sense, as there is no multithreading).

    One thing I'm concerned about, however, is IE's memory leaking.

    Mark Wubben - 26th May 2004 08:43 - #

  3. Thanks for drawing attention to the IE Mac problems. These problems are the reason that I don't yet use either addEventListener or attachEvent.

    Now for the next onload problem:

    onload takes too bloody long to fire. Especially when a page needs many images from a slow server, it can take infuriatingly long to execute the nifty script that adds behaviour to the page.

    Foldout menu's don't yet work as long as one image on the page hasn't yet loaded completely, image replacements or column adjustments have to wait for this same image.

    What shall we do about it? We need onLoadExceptForImages, or maybe onBodyElementRendered. I tried to call a function just before the body tag, like

    <script>
    tweakPage()
    </script>
    </body>

    but

    1. it violates the separation of behavior and structure
    2. it gave serious problems in some browsers

    How shall we solve this next onload problem?

    ppk - 26th May 2004 10:49 - #

  4. How about a small 'loading' DIV that displays when the page is first shown? You could enclose the actual content within a 'loaded' DIV which has display set to 'none' by default, and then swap the display of both DIVs once the document has loaded. I dunno about cross-browser compatibility; I'm no expert on JavaScript. This solution just seems to be a good idea.

    Aankhen - 26th May 2004 11:09 - #

  5. I'd say rather than onBodyRendered, a better (or more accurately, more commonly used) type would be onDomLoaded. I don't really care id the document has enough preparation done to render or has loaded all images yet, I only care that I can add my hooks in / start loading other resources, etc.

    Lach - 26th May 2004 12:06 - #

  6. I guess that most people on't really like JS as mentionned :

    - it can be deactivated which is a trouble because you have to decide either to disable the access to your website to those who don't enable JS, or to double your work by doing a non JS version

    - it is messy to code with, I'd rather like a real development platform client side that allows me to debug, etc... simply ! debugging JS is one of the worst thing to do

    - the standard is less followed than XHTML or CSS themselves by browser and it is far too complex to ensure that your JS code will run everywhere

    I like the client side processing idea but JS is far from being an efficient solution IMO.

    Obviously, I'm not a JS guru.

    - Lawouach

    Lawouach - 26th May 2004 12:54 - #

  7. On my article to ALA I provide a function called listen. It is a wrapper to the addEventListener functionality which has that interesting property that it'll pass a faux Event object to IE, even though it assigns the handler to the event via attachEvent.

    As I state in the article comments, it could be expanded so that when the browser does not support addEventListener nor attachEvent, instead of throwing, it assigns the function in the ordinary object.onevent way. Eventually I came to the idea that if I used closures, I could pass the faux Event object and emulate the behavior of handlers assigned via addEventListener to browsers as old as Mac IE 5. I never got to implement it, but now looks like a good time to do it. I'll get to it and post it in the comments later.

    The advantage of it, really, is the faux Event object. With it, you can handle the parameter passed to the handler function (almost) as it were assigned via addEventListener. Unfortunately, some things such as control over bubbling, or removal of handlers is still impossible.

    Caio Chassot - 26th May 2004 13:09 - #

  8. Here's a test case. It's not thoroughly tested and might need further tweaking. Anyway, without the faux event wrapping Win IE proprietary event model, it has a very diminished use. Well, at least you still can access the event source via e.currentTarget.

    Caio Chassot - 26th May 2004 13:38 - #

  9. ppk, what problems and in which browsers have you had them when calling a script before ending the body?

    Caio Chassot - 26th May 2004 13:41 - #

  10. The problem browser was Safari. I set the height of a few columns to the height of the highest among them onAlmostLoad, but Safari read the height as 0, which gave a very nasty effect.

    No doubt this is solvable, if I'd have the time to do some research, but I assume that such small but fatal bugs will creep up as we use pseudo-onloads more and more.

    I'm not saying it's impossible to devise an onAlmostLoad event handler, but I do say it has to be researched very thoroughly. Besides, even when it works perfectly you still write JavaScript code in an XHTML file, which we shouldn't do.

    ppk - 26th May 2004 14:11 - #

  11. ppk, writing js in the xhtml is not a good practice, but if you know what you're doing, I'd compare using a single script block with a single function call in the end of the body to using a simple layout table in css design: it's not ideal, but it might be a much much simpler solution than the purist counterpart, or maybe even the only solution.

    Caio Chassot - 26th May 2004 14:39 - #

  12. Sure, but I'm going to search for a solution that doesn't require the single function call. I'm not yet sure if I'll actually find it, but searching for it won't hurt.

    ppk - 26th May 2004 14:59 - #

  13. Okay, let's do a quick summary of what you have to work with in the various browsers:

    • document.addEventListener('load',singlehandler,false): MSN/OSX, op7.50, saf1.2, ow5b6.1
    • window.addEventListener('load',singlehandler,false): moz, saf1.2, ow5b6.1
    • document.attachEvent('onload',singlehandler): op7.50
    • window.attachEvent('onload',singlehandler): op7.50, ie5.0w, ie5.5w, ie6w
    • document.onreadystatechange=function(e){ if(/mac/i.test(navigator.platform)? document.readyState!='interactive': document.readyState!='complete')return;allhandlers}: ie5m, MSN/OSX, ie5.0w, ie5.5w ie6w
    • window.onload=function(){allhandlers}: all

    There. We can use onreadystatechange in iem, window.attachEvent in iew, document.addEventListener in all modern browsers but moz, and window.addEventHandler in moz. window.onload is a fallback. onreadystatechange is very unlikely to be used before, but that's the weak point - we can't rely on that any more than we can rely on window.onload not being overwritten later on.

    liorean - 26th May 2004 17:40 - #

  14. s/window.addEventHandler/window.addEventListener/

    liorean - 26th May 2004 17:45 - #

  15. I've just been using Dithered.com's DOM2 Events script to emulate DOM2 on many platforms, and saying screw you to patforms that don't support DOM2 Events. Though I guess that script probably is a bit too weighty for many folks.

    k - 28th May 2004 00:07 - #

  16. That's really complicated. This is easier, smaller, and doesn't use recursion:

    
    window.onload = function () {
    	for(var ii = 0; arguments.callee.actions.length > ii; ii++)
    		arguments.callee.actions[ii]();
    };
    window.onload.actions = [];
    

    Just add new functions with window.onload.actions.push(fnName) . Couldn't be simpler, and there is no possibility of IE closure bugs.

    Jimmy Cerra - 29th May 2004 19:27 - #

  17. Jimmy: the problem with techniques that build up an array of functions and then call them all once the page has loaded is that you'll still over-ride a script that has already assigned something to window.onload. If that isn't a concern then your technique works perfectly.

    Simon Willison - 29th May 2004 20:28 - #

  18. That's a good point. One solution is to add the initial onload function to the actions array when it was created. This is easy to code in a more general solution for any event (exercise for the reader). Another problem with my code is that Array.prototype.push is undefined in MSIE 5.0! I completely forgot about that. Oh well.

    P.S. I tried calling document.getElementsBySelector() in one of MSIE's DHTML Behaviors. It didn't work, and I don't know why.

    Jimmy Cerra - 30th May 2004 03:09 - #

  19. Where I work we use an array called onloadHandlers which is iterated though after onload() via eval() calls. This, paired with an onload() "checking" script (eg. if onload is already defined, assign to a temp. function as someone mentioned earlier - call that, then check onloadHandlers) would work nicely. I don't generally like eval() though as I hear it's just slow ;)

    In some of my own work I have adopted a similar technique, but instead of strings I use function references. ie. onloadHandlers[onloadHandlers.length] = someOnloadFunction; - then call those directly from onload().

    Despite some lack of support for addEventListener etc., I wrote a script similar to Scott Andrew's that allows addEventListener/removeEventListener to be used as a generic interface. It's not perfect, but works for my uses. (schillmania.com/script/addeventhandler.js)

    I found also that you can parse and modify DOM elements before onload() if you make a call to a function from a script block positioned at the end of the document, just before the body close tag. Since getElementById() doesn't work before body.onload(), I use a home-brew "getElementsByClassName" function to grab elements and modify them before the document load. This way you don't have to resort to counting indices of elementsByTagName() looking for an attribute to find a specific item, etc. I tried to optimize it so multiple calls don't bog down the browser (the looping etc. can add up,) by restricting searches to a certain tag name or parent element (or both).

    Scott Schiller - 2nd June 2004 18:44 - #

  20. Hi, I like the function. Haven't tested thouroughly, (I'm suffering from the effects of a freshly poured cocktail), but doesn't it need this ? if (window.onload) var oldonload = window.onload; else {window.onload=func; return true;} to prevent it from bombing if not onload function has already been assigned? Serge

    Serge d'Adesky - 4th June 2004 01:11 - #

  21. I take it you've read this related blog post by the Microsoft Scripting dude Eric Lippert?: http://blogs.msdn.com/ericlippert/archive/2003/12/ 12/53454.aspx

    Marcus Tucker - 6th June 2004 02:24 - #

  22. Another problem with the "<script>tweakPage()</script> just before </body>" method can occur in IE/Win.

    IE does not like you using some DOM methods on an object it hasn't finished parsing yet, and can occasionally and randomly either produce JavaScript errors or crash when you try.

    So because the tweakPage() action occurs before the element <body> is closed, actions such as trying to appendChild a new node to the document.body are potentially dangerous. (However, altering the contents of completed child elements of the body seems to be fine in my experience.)

    A workaround is:

    <script type="text/javascript">
      setTimeout(tweakPage, 0);
    </script></body></html>

    Normally, the timeout here would allow IE to exit the script block and parse the remainder of the document before triggering the tweakPage function.

    It is conceivable but massively unlikely that on a really slow connection, if the packet boundaries fall in just the wrong place, IE might call the timeout before parsing the end of the document. Probably no real solution to this.

    (Normally I would put the setTimeout at the bottom of the linked script doing the work rather than embedding.)

    Another approach I've occasionally used in places where I prefer to keep <script>s inside the <head> only is to include a marker <div id="end"></div> just at the end of body, then used a setInterval function to sniff for it, and kick off the initialisation functions when document.getElementById('end') returns non-null.

    Andrew Clover - 6th June 2004 13:49 - #

  23. Hello Simon and others.

    I have thoroughly enjoyed your blog, Simon, since discovering it 6 months ago. I have learned a great deal.

    I am working on a project in which I want to implement unobstrucive javascript. To those ends, I have combined you and Scott's approach for handling events. I have also generalized your function.

    It works on the Mac with firefox 0.8, Mozilla 1.7rc2, Netscape 7.1 and IE 5.2. It doesn't work with Opera 6.0. Safari 1.1 seems to hate my test page. Therefore, I am unsure about it.

    I haven't had a chance to try it on win IE and Opera.

    Here is the code:

    // Marlon's combination of Scott and Simon's work function addEvent(elem, evType, func, useCapture) { if( elem.addEventListener ) { // Marlon: the W3C DOM approach window.alert("W3C approach"); elem.addEventListener(evType, func, useCapture); return true; } else if( elem.attachEvent ) { // Marlon: the IE DOM approach window.alert("IE approach"); var r = elem.attachEvent("on" + evType, func); return r; } else { // Marlon: Simon's approach window.alert("Simon's approach"); var onEvt = "on" + evType; var elOldEvFuncs = elem; if( (typeof elem[onEvt]) != 'function' ) { elem[onEvt] = func; } else { elem[onEvt] = function() { elOldEvFuncs(); func(); } } } }

    What do you think?

    P.S. Pardon me, I can't seem to get the markup correctly for the code.

    Marlon A. Griffith - 9th June 2004 19:47 - #

  24. For Netscape Communicator 4.75 I get

    JavaScript Error: http://simon.incutio.com/code/js/addloadevent/, line 20:

    document.body has no properties.

    BTW: A better spamfighting technique would use mail.

    -HJC

    Henry Cobb - 6th July 2004 19:19 - #

  25. Just a *realy* minor point:

    jslint ( http://www.crockford.com/javascript/jslint.html ) complained (the error was: "Expected ';' and instead saw '}'") about the section

    window.onload = function() {
      oldonload();
      func();
    }
    

    After a quick email to the jslint author Douglas, he pointed out that every asignment statement must end in a semi-colon so the fixed code reads:

    
    window.onload = function() {
      oldonload();
      func();
    };
    

    Richard@Home - 30th July 2004 16:30 - #

  26. Dare I ask if you have tried using this within a frameset where you have functions within the frameset and the child frames that require registering?

    fraser crichton - 5th August 2004 23:42 - #

  27. Simon, I would like, if you agree on that, to include your addEvent function above into a JS file I am going to release under MPL. The license header could contain the following statement "The _addEvent function's code is Copyright (C) Simon Willison 2004.". Let me know :-) Thanks!

    Daniel Glazman - 29th August 2004 22:22 - #

  28. Has anyone tried this technique with ASP.NET? I found that it stopped any rendering of html tags....

    Clive Hill - 21st September 2004 11:12 - #

  29. Hello all,
    This post just for saying that after a lot of effort with onload event, i've trying Alex Russell solution (sigslot_core from netwindow package) , that's wonderful i just wanna know if there's any drawbacks ?

    synopsis

    __sig__.connectByName(null, "onLoad", null, "observer");

    description : observer will be executed just after onload function

    Stephane T. - 6th October 2004 11:15 - #

  30. window.onload = function(){ // speed, dragHeight, trackHeight, trackObj, upObj, downObj, dragObj, contentMaskObj, contentObj myScroll = new ScrollObj(3,20,216,"track","up","down","drag","con tentMask","content"); }; how rewrite this js with the AddLoadEvent stuff??

    _com - 13th January 2005 03:12 - #

  31. Here's the window.onload code that I've used for several years - it's not clever, but it works in all browsers...

    var OnLoad = ""; window.onload = function() { eval(OnLoad); }

    To add a load event:

    Onload += "functionName();"

    Great blog - separating markup, style and functionality is the way to go!

    Ceeb - 8th February 2005 08:40 - #

  32. Hi, The following fires 3 functions in succession, for click event of a button with id btnClick. The code demonstrates that it is possible to wire up multiple event handlers for any event with some amends to Simon's excellent code above. note i haven't defined the test functions or the button for brevity, simply add with alert statements to test. -----
    eventAddHandler("btnClick", "onclick", test);
    eventAddHandler("btnClick", "onclick", test2);
    eventAddHandler("btnClick", "onclick", test3);
    function eventAddHandler(eventSourceId, eventType, func)
    {
    var eventSource = document.getElementById(eventSourceId);
    var eventRef = eventSourceId + "." + eventType;
    var eventHandlers = eval(eventRef);
    if (typeof eventHandlers == 'function') { // not first handler
    eval(eventRef + " = function() {eventHandlers(); func();}");
    } else { // first handler
    eval(eventRef + " = func;");
    }
    }

    Keith - 28th February 2005 22:43 - #

  33. I am working on writing something that I can use to "register" functions and will not allow them to be run (like clicking on a button) until the page is fully loaded. Any ideas??

    Nola - 1st March 2005 17:23 - #

  34. http://blog.keithpatton.com/MultipleEventHandlerFu nctionsForJavascriptEvents.aspx

    Please take a look at this. I have made an example which extends Simon's code above to provide a cross-browser generic way to add multiple functions to any javascript event.

    Nola, you may be looking for something much simpler which is to fire a set of functions when the page loads.

    <script type="text/javascript">
    window.onload = function() {
    functionOne();
    functionTwo();
    }
    </script>

    My example allows you to add functions to any event from multiple locations within the page. All comments gratefully appreciated.

    Keith Patton - 1st March 2005 22:39 - #

  35. I did run into a problem when attempting to pass in a function that is part of another class such as:
    function Tester(foo,bar) {
        this.FooBar = Tester.FooBar;
        this.foo = foo;
    }
    Tester.FooBar = function() {
        alert("Tester.FooBar() called");
        alert(this.foo);
    }
    var t = new Tester('hello');
    addLoadEvent(t.FooBar);
    
    When you run the above code you will see that the 'this' within the Tester class appears to reference the window object and not itself. Any workarounds for this? Thanks.

    Jason - 18th March 2005 03:47 - #

  36. Hi, The actual object instance is out of scope when the event is fired, so the actual function called is Tester.FooBar. If you call this direct, you get this same results (this.foo returns as undefined).
    The solution to this, is simply to use a standard function to wire up against any event, and then do any working with objects from inside there.
    http://blog.keithpatton.com/CommentView,guid,ea208 13e-f68b-47ad-a2a9-8ef390adfe1d.aspx contains some sample code which work.

    Keith Patton - 18th March 2005 10:41 - #

  37. Keith, thanks for the workaround. I did find an alternate solution using a tip at: http://w3future.com/html/stories/callbacks.xml

    Here is the alternate solution which seems a bit more elegant to me:

    function Tester(foo,bar) {
        var self = this;
        self.foo = foo;
        
        self.FooBar = function() {
            alert("Tester.FooBar() called");
            alert(self.foo);
        }
    }
    
    var t = new Tester('hello');
    addLoadEvent(t.FooBar);
    

    Jason L - 18th March 2005 13:26 - #

  38. A few days ago I started to look at this problem of cross browser event managment. I wanted a solution with this type of simple usage:

    document.onclick = createCallbackManager([optional funcRef]);
    ...
    document.onclick.addCallback(funcRef);
    ...
    document.onclick.removeCallback(funcRef);
    

    And today I actually found one! It's light, simple and fast... However I'm no expert, so I'm posting it here to get a judgement.

    
    /**
    * Title: CallbackManager
    * Description: Lightweight event-management.
    * Copyright: Andreas Karlsson 2005
    * @author Andreas Karlsson
    * @version 1.0
    *
    * A global function which creates and returns a "callback-manager" that can be
    * directly assigned to any browser event (i.e. document.onclick). The call
    * to createCallbackManager can include an optional number of arguments,
    * in the form of function-references, which will be added to this managers 
    * list of callback-functions. 
    *	After this initial assignment the event supports two simple functions; 
    * addCallback(functionRef) and removeCallback(functionRef). Both taking
    * a function-reference (possibly anonymous) as argument, and both having 
    * a time-complexity of O(1).
    *
    * Example of usage (both examples do the same thing):
    * 	document.onclick = createCallbackManager();
    *	document.onclick.addCallback(funcRef1);
    *	document.onclick.addCallback(funcRef2);
    *	document.onclick.removeCallback(funcRef1);
    *	[OR]
    * 	document.onclick = createCallbackManager(funcRef1, funcRef2);
    *	document.onclick.removeCallback(funcRef1);
    *
    * The inner workings of this mechanism is as follows. createCallbackManager()
    * is actually an object constructor. The object has one private attribute;
    * listenerArray and one public method; invoke(e). The invoke method in turn
    * has the add and remove functions as attributes, which still share scope with 
    * listenerArray.
    * When createCallbackManager is called as a function it creates a
    * createCallbackManager object, and returns a reference to it's invoke
    * method. No reference to the object is maintained. When an event fires the
    * registerd invoke method is executed, which in turn calls all the references
    * in listenerArray.
    * 
    * NOTES: So far this has only been tested with IE6 and Firefox, no apparent
    *	problems. However no tests have considerd speed or memory leaks.
    */
    function createCallbackManager(callbacks) {
    	// If called without "new", create object and return it's invoke method.
    	if(!this.isObject) {
    		return (new createCallbackManager(arguments)).invoke;
    	}
    	
    	var listenerArray = new Array(); // Hashmap of callback functions.
    	
    	// If callbacks were included at object creation, add them!
    	for(var i = 0; i<callbacks.length; i++)
    		listenerArray[callbacks[i].toString()] = callbacks[i];
    	
    	// Call all the callbacks.
    	this.invoke = function(e) {
    		e = e ? e : window.event;
    		e.properThis = this;
    		for(fName in listenerArray) {
    			listenerArray[fName](e);
    		}
    	}
    	
    	this.invoke.addCallback = function(callback) {
    		listenerArray[callback.toString()] = callback;
    	}
    	
    	this.invoke.removeCallback = function(callback) {
    		delete listenerArray[callback.toString()];
    	}
    }
    createCallbackManager.prototype.isObject = true; // Only used to test if in construction or not.
    

    That's it! I also have to add a disclaimer; I haven't looked around much, maybe this is a standard solution, I wouldn't know...

    Andreas Karlsson - 25th March 2005 23:22 - #

  39. I was interested in this solution, especially for the way it will work with previously registered onload events. However, it does not seem to work if I have something else registered this way:
    < body onload="somepagefunction()" >
    I have a standard javascript file that is going to be included in every page of a website, some of which already have body onload functions declared. I assume it is failing right now because my javascript is running and looking for the contents of window.onload before it actually gets to the body tag (I get null at the point my javascript is loading, but I do get the correct function later if I check it from a click event). Aside from begging to get my javascript file included in the page footer instead of the header, is there anything else I can do to make sure I find and execute existing onload functions registered in the body tag?

    Julie - 20th April 2005 02:21 - #

  40. Excellent, i solved my problems from this message.i am very thankful to this site. Bye and I hope always help me.

    vijay - 23rd April 2005 11:47 - #

  41. Just an adaption of a previously mensioned. I adapted it because now it adds the previously added functions to the onload. And this bit of script can be placed on the page multiple times. I needed that, because I am using a dynamic language tocreate the pages, not nowing how many times I include the script.

    if(!OnLoad){
    var OnLoad = window.onload;
    var OnLoad = ",dynamicallyNamedFunction()";
    }else{
    OnLoad += ",dynamicallyNamedFunction()";
    }
    window.onload = function(){ eval(OnLoad); };

    Peter van Westen - 15th May 2005 22:32 - #

  42. The following code executes before the page is loaded and consequently doesn't work. Yet if I use addLoadEvent() it works correctly. Why is this?
    <html>
    <head>
    <script type="text/javascript">
    function init() {
    	document.body.appendChild(document.createTextNode( '  World!'));
    }
    
    window.onLoad = init();
    </script>
    </head>
    <body>
    <p>Hello,</p>
    </body>
    </html>
    
    Same result on IE6 and Firefox...

    Leon Stringer - 20th June 2005 22:59 - #

  43. Leon: you're executing the init() function when you should just be passing a reference to it. Use this line instead and your code should work just fine:
    window.onLoad = init;

    Simon Willison - 20th June 2005 23:32 - #

  44. Simon, thanks for spotting my elementary error.

    I should point out that it seems to have to be window.onload (not onLoad) to work (for me at least).

    Leon Stringer - 21st June 2005 11:55 - #

  45. so now we have tackled the onload event what happens with the rest: document.getElementById('something').onclick = function() { whateverFunction() };

    that for instance doesn't work.

    Justin Halsall - 9th July 2005 00:07 - #

  46. Here's the window.onload code that I've used for several years - it's not clever, but it works in all browsers... var OnLoad = ""; window.onload = function() { eval(OnLoad); } To add a load event: Onload += "functionName();" Great blog - separating markup, style and functionality is the way to go!

    dirk antel - 10th October 2005 20:37 - #

  47. I found Andrew's comment (http://simon.incutio.com/archive/2004/05/26/addLo adEvent#comment22 ) very useful. I had some page display issues related to the body onload event that was driving me nuts and Andrews comment "IE does not like you using some DOM methods on an object it hasn't finished parsing yet, and can occasionally and randomly either produce JavaScript errors or crash when you try." was a lifesaver.

    I found a better way though than the suggested setTimeout method. Instead simply add a 'defer' attribute to the script element.

    This causes the browser the defer the execution of the script until the browser has loaded the DOM. There was a visible difference between the timeout and defer approach. My javascript basically resizes a table. With the timeout I can see the original table and after a brief moment i then see the adjusted table. With the defer option , only the resized table is displayed.

    Here is useful link that discuss the defer attribute.

    http://www.websiteoptimization.com/speed/tweak/def er/

    Sanjiv

    Sanjiv Jivan - 15th October 2005 20:09 - #

  48. Pardon my ignorance. Do you need an independent call in the document head to the add event script, or do you append the add event script to existing javascript files? Thanks from a relative rookie, Eric Hobart

    Eric Hobart - 2nd November 2005 21:39 - #

  49. Thank you, oh unknown JS guru. You just fixed an ugly bug in my app :-D

    Magnus Akselvoll - 4th November 2005 09:33 - #

  50. Thanks. That's a useful bit of javascript. I added it to my common.js.

    Lance Fisher - 11th January 2006 03:06 - #

  51. hi

    satish - 25th January 2006 09:15 - #

  52. dfgfdgdfgdf

    dfgdfg - 10th February 2006 15:00 - #

  53. I've modified Simon's "addLoadEvent" function above to allow any function to be attached from within the head section of an HTML document, i.e. prior to the "onload" event being defined. The order in which the functions are attached vary from browser to browser, but if that's not important to you, then this script can be useful. If someone knows of a way to get this script to work in IE5.2 on the Mac, please let me know.

    function addLoadEvent(_function) { var _onload = window.onload; if ( typeof window.onload != 'function' ) { if ( window.onload ) { window.onload = _function; } else { var _addEventListener = window.addEventListener || document.addEventListener; var _attachEvent = window.attachEvent || document.attachEvent; if ( _addEventListener ) { _addEventListener('load', _function, true); return true; } else if ( _attachEvent ) { var _result = _attachEvent('onload', _function); return _result; } else { //todo: preloading fix for ie5.2 on mac os return false; } } } else { window.onload = function() { _onload(); _function(); } } } function a() { alert('func a'); } function b() { alert('func b'); } function c() { alert('func c'); } addLoadEvent(c);

    body onload="a();b();"

    Hello World!

    function d() { alert('func d'); } addLoadEvent(d);

    Reginald Mullin - 28th April 2006 04:04 - #

  54. I think you mean oldonload, not oldunload.

    Joe Grossberg - 30th May 2006 00:05 - #

  55. Hi there,

    Thank you for sharing this great bit of code.
    I may be missing something really obvious, but when I use it, I get an error in Firerfox (1.5), IE(6.02) and Opera(8.53).. .however, everything works fine. IE and Firefox report that "func() is not a function". Opera's JavaScript console is actually a bit more informative. It tells me that I have a "Type mismatch (usually a non-object value used where an object is required)" at the same line in the code that the other browser's don't appear to like.

    I can't work out what is wrong, and I haven't spotted a similar error mentioned in this list of comments.
    OK, so my functions work fine... but it makes me jumpy when I still get an error message! Any pointers would be greatly appreciated.
    Roll on @media 2006!

    thanks in advance,
    Francis

    Francis - 6th June 2006 10:35 - #

  56. I have the same issue as Francis ... it seems to work fine, but get an annoying error ("type mismatch" in IE) at the line "var _result = _attachEvent('onload', _function);". Anyone know how to get rid of the error?

    ed ross - 14th June 2006 15:19 - #

  57. Based on the new browser-independant DOM Loaded solution, I've put together an addLoadEvent-inspired function, addDOMLoadEvent:

    http://www.thefutureoftheweb.com/blog/2006/6/adddo mloadevent

    Jesse Skinner - 22nd June 2006 06:41 - #

  58. Thanks for this info, works great in the body-tag ...

    Jay - 25th July 2006 23:10 - #

  59. This is a cool script, but I had a page where I wanted to pass in a function that required variables specified that were specific to the page. (All my other scripts are in a common scripts.js file.) This is how I got it to work:

    function picParams() {
    	addPicFunc(360,270,640,480);
    };
    addLoadEvent(picParams);
    

    I'm sure a better scripter than myself could find a better way to encapsulate that, but in the meantime, it allows me to use your great script more flexibly. :)

    Stephen Rider - 22nd August 2006 21:57 - #

  60. New lines are not converted to breaks; use paragraph tags instead. XHTML must be well formed. The following tags are allowed: a, p, blockquote, ul, ol, li, dl, dt, dd, em, strong, dfn, coYour Name: (required) Your Email: Your URL (optional, trumps email for display purposes): Comment: de, q, samp, kbd, var, cite, abbr, acronym, sub, sup, br, pre

    hjkjh - 25th September 2006 11:50 - #

  61. 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 */ });

    aa - 25th September 2006 11:51 - #

Comments are closed.

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

A django site