dean.edwards.name/weblog/2005/09/busted

The window.onload Problem – Solved!

Well, when I say solved I mean solved for the two most important browsers – Internet Explorer and Mozilla/Firefox. Still that’s good enough isn’t it?

First, let me define the problem. The window.onload event is used by programmers to kick-start their web applications. This could be something trivial like animating a menu or something complex like initialising a mail application. The problem is that the onload event fires after all page content has loaded (including images and other binary content). If your page includes lots of images then you may see a noticeable lag before the page becomes active. What we want is a way to determine when the DOM has fully loaded without waiting for all those pesky images to load also.

Mozilla provides an (undocumented) event tailor-made for this: DOMContentLoaded. The following code will do exactly what we want on Mozilla platforms:

// for Mozilla browsers

if (document.addEventListener) {
	document.addEventListener("DOMContentLoaded", init, false);
}

So what about Internet Explorer?

IE supports a very handy (but non-standard) attribute for the <script> tag: defer. The presence of this attribute will instruct IE to defer the loading of a script until after the DOM has loaded. This only works for external scripts however. Another important thing to note is that this attribute cannot be set using script. That means you cannot create a script using DOM methods and set the defer attribute – it will be ignored.

Using the handy defer attribute we can create a mini-script that calls our onload handler:

<script defer src="ie_onload.js" type="text/javascript"></script>

The contents of this external script would be a single line of code to call our onload event handler:

init();

There is a small problem with this approach. Other browsers will ignore the defer attribute and load the script immediately. There are several ways round this. My preferred method is to use conditional comments to hide the deferred script from other browsers:

<!--[if IE]><script defer src="ie_onload.js"></script><![endif]-->

IE also supports conditional compilation. The following code is the JavaScript equivalent of the above HTML:

// for Internet Explorer

/*@cc_on @*/
/*@if (@_win32)
	document.write("<script defer src=ie_onload.js><\/script>");
/*@end @*/

So far so good? We now need to support the remaining browsers. We have only one choice – the standard window.onload event:

// for other browsers

window.onload = init;

There is one remaining problem (who said this would be easy?). Because we are trapping the onload event for the remaining browsers we will be calling the init function twice for IE and Mozilla. To get around this we should flag the function so that it is executed only once. So our init method will look something like this:

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;

	// do stuff
};

And that, as they say, is that.

I’ve provided a sample page that demonstrates this technique. Start copying.;-)

Update. Safari now supported!

Comments (134)

Leave a comment

Copied!:-)Thank you so much, Dean. I never would’ve even explored other options than the window.onload method if it wasn’t for guys like you!

  • Comment by: 4rn0
  • Posted:

defer is part of HTML4

“defer [CI] When set, this boolean attribute provides a hint to the user agent that the script is not going to generate any document content (e.g., no “document.write” in javascript) and thus, the user agent can continue parsing and rendering”

Erik – OK. I didn’t realise that. The real point is that only IE supports this attribute.

  • Comment by: -dean
  • Posted:

This is good news, and I assume that neither methods will be disappearing from browsers anytime soon (they seem pretty well rooted)

I’m sure Opera must have a feature like the firefox one too, I know that is a completely unresearched statement, but considering the forward thinking standards based motivation of Opera and the fact it supports ‘user scripts’ and ‘browser scripts’ and plugins means that the must have allowed away for code to be executed after the dom loads. Mustn’t they?

Anyway, awesome work.

  • Comment by: Jon B
  • Posted:

Re: Erik Arvidsson

Part of HTML 4? hmm, maybe that means it will become unavailable if IE starts respecting doctypes better?

  • Comment by: Jon B
  • Posted:

About defer: interesting thing to note: it is #implied. See http://www.w3.org/TR/html401/interact/scripts.html#adef-defer.

On to your test. Is the following expected ? In Gecko browsers (tested firefox 1.5 beta and the latest nightly trunk build) the red and white message appears before the image has started loading. Other (Mac) browsers wait for the image to load fully before showing the message.

  • Comment by: Philippe
  • Posted:

Philippe – the test page works as expected. There is not much DOM content so the message should appear very quickly (almost instantly). That is pretty much the point of this exercise.

  • Comment by: -dean
  • Posted:

Cross-browser onDOMContentLoaded An elegant solution to the window.onload problem in Mozilla and IE. Client-side developers have long been frustrated by the shortcomings of window.onload, namely that it takes too long before firing if you want to attach scripts to rework the DOM, yet you

  • Trackback by: InfoSauce
  • Posted:

Treating defer as equivalent to DOMContentLoaded seems pretty risky to me. DOMContentLoaded is a promise: the full DOM is available to you when it fires. defer is just your promise to the rendering engine, that the content of this script doesn’t depend on being loaded before following content, not their promise to load it at a particular time. Try trickling out your HTML with a PHP script that sends an element, then sleeps for a second or two, and based on my memory of playing with defer you’ll find your script loaded as soon as IE gets a chance, not after the DOM is fully loaded.

Thanks, Dean. This is indescribable awesome if it works dependably. Of course, if it doesn’t doing the same (the IE piece) using an .htc isn’t the end of the world, but it seems there are more hoops to jump through to get that set-up right, so this is more elegant.

  • Comment by: Tim Connor
  • Posted:

Phil – I did as you suggested and it seems to still work. I’ve also used this technique on a page with multiple (50-60) behaviors attached and it waits for those to load too.

  • Comment by: -dean
  • Posted:

Tim – you are right. You can also use an HTC and trap the ondocumentready event. As you point out, not everyone is able to deliver HTCs from their server.

  • Comment by: -dean
  • Posted:

Dean: could you write up the HTC method too? The more options the better.

[…] davidbisset.com Javascript: window.onload Problem Fixed in IE and Firefox The problem is that the onload event fires after all page content has loaded (including images and ot […]

there is another way… stick a call to your init function at the bottom of the page after your body tag. a lot simpler than tagsoup

  • Comment by: taxexile
  • Posted:

Right you are: I should test things, rather than rely on my increasingly horrible memory. I was actually using it for loading script from unreliable hosts, where I didn’t care if it came early or late, as long as it didn’t hang the page when it didn’t come at all. Trickling out a page says it works exactly like you say, delaying the load whether or not IE has time and connections to spare.

taxexile – I’ve tested your technique in the past and it is not 100% reliable (more like 98%).

  • Comment by: -dean
  • Posted:

Simon – I’ll document the HTC approach in a subsequent post – I didn’t want this post to be burdened with too many technicalities.

  • Comment by: -dean
  • Posted:

The ‘defer’ attribute is a tricky little beast. Beware of dependencies between scripts when using the ‘defer’ attribute. i.e. if the ‘onload’ script in file2.js depends on functions defined in file1.js, you should avoid adding a ‘defer’ attribute to the script tag for file1.js.

The problems caused are infrequent, but are easily summarized by saying you cannot guarantee functions will be available from a deferred script until after ‘onload’ fires. Which is what you were using ‘defer’ for in the first place.

If everything’s in one big ‘defer’ script, you should be fine though.

And, what was the problem with using ‘onreadystatechange’ for Internet Explorer again?

  • Comment by: ash
  • Posted:

ash – you are probably right. It is safer to have only one deferred script.

As to your other point – the readyState property is unreliable. I’ve tested this extensively in the past and it only leads to disappointment.

Anyway, time for bed now – it’s 3.00am;-)

  • Comment by: -dean
  • Posted:

this *is* huge. I’ve been using a nasty CSS hack that applies “display: hidden” to the entire document and let an onload handler toggle it on to do some weird DOM stuff before the doc is displayed. Is the code in these event handlers guaranteed to run before the rendering phase?

My Wishlist for Browser

I filed a bug about this in Safari’s bug database.

dean: if 2% of users were using Safari then we’d be at the same level (if your stat were even accurate!). this is just tag soup though isn’t? we’ll be using buffer-overflows in html next to trigger something next!! stick a script block at the bottom and let that do it. i think this ‘solution’ is ridiculous. love the htc-mozilla bridge though

  • Comment by: taxexile
  • Posted:

taxexile – I don’t think you understand the problem. Simply putting a script tag at the bottom of the page is not sufficient. Remember that the DOM needs to be loaded and parsed. PPK and others have explored your solution in the past and found it wanting.

  • Comment by: -dean
  • Posted:

taxexile – I’m curious how, in any way shape or form, this is “tag soup” a very specific and completely unrelated problem (a script tag at the bottom of the page is closer to “tag soup” and has various shortcomings that have been addressed ad nauseam by many of us). This can help reduce tag soup, actually.

Do you mean hacky? If so I still disagree, as this is the most elegant soultion to a problem that has been bothering many of us for quite a while, but at least that makes sense.

  • Comment by: Tim Connor
  • Posted:

Simon – it might actually be a feature to NOT have Safari supported. I’ve read about some problems in scripts due to how Safari renders the page before images are loaded. I didn’t confirm it myself, but it sounded like it was something the sIFR people encountered (link in my blog).

Of course, even if it really is a problem, it’d be nice to have some way to do this in Safari if/when it ever gets resolved.

  • Comment by: Tim Connor
  • Posted:

Thanks, Dean

“DOMContentLoaded” is just the thing I really need!

BTW, you may try to use document.onreadyStateChange to call the function init() for internet explorer.

  • Comment by: Hedger
  • Posted:

> Well, when I say solved I mean solved for the two most important browsers – Internet Explorer and Mozilla/Firefox. Still that’s good enough isn’t it?

Nope, not quite good enough:)But here’s something I’ve been working on:

function onready(el, func){
	this.args = new Array(el, func)
	this.doTry = function(){
		try{
			var el = eval(this.args[0])
			el.onloading = this.args[1]
			el.onloading()
			clearInterval(this.args[2])

		}
		catch(e){}
	}
	this.doTry.bind = function(object){
		var method = this;
		return function(){
			method.apply(object);
		}
	}
	this.args[2] = setInterval(this.doTry.bind(this), 250);
	return this
}
new onready("document.body", function(){this.onclick=function(){alert("You good thing")}})
  • Comment by: Adrian D.
  • Posted:

Dear Dean,

I really appreciate your finding and had made an example on http://www.hedgerwow.com/360/dhtml/before_onload.php with some of your comments and codes.

Pleae let me know if you have any concern.

Thanks.

  • Comment by: Hedger
  • Posted:

Window.onload Fixed Dean Edwards does it again. He’s fixed the window.onload problem for Firefox and IE. What is the window.onload problem you ask? A fair enough question. window.onload fires after the page is loaded, allowing developers to init their applications. …

  • Trackback by: Blog-Fu
  • Posted:

links for 2005-09-26 Turn your world LDAP-tastic (tags: LDAP) The window.onload Problem – Solved! (tags: javascript) mezzoblue § Web Server Backup (tags: rsync) OfficeSlang.com – Funny Office Slang (tags: funny) LinksysWrt54g – SeattleWireless (tags: diy wrt54g hack) Con…

I know I’m just being fussy here, but shouldn’t it be

defer="defer"

in XHTML?:-)

Nick – bearing in mind that we are using conditional comments/compilation I don’t think this is an issue.

  • Comment by: -dean
  • Posted:

This windows.onload solution breaks the clearfix solution for ie: http://www.positioniseverything.net/easyclearing.html

Mmm, do not know why ..

Arjen

  • Comment by: Arjen
  • Posted:

ppk said:

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.

And the example he used was:

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

I wonder if the same problem exists if that tweakPage() is put after the ending body tag:

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

I don’t have a Mac, so I can’t test this out… But I’d like to have a solution that includes non-IE and non-Gecko browsers.

  • Comment by: Maian
  • Posted:

I’ve had a different problem with onload — that of multiple, independent scripts all having their own separate initializations, each specifying “onload”. Since IE’s support of event-listener varies too much to be reliable, i’ve instead created an “initFunctions” array:; every function that needs to get initialized gets pushed onto that array, then the final onload simply iterates through the functions.

This issue has been far more critical as an onload handler problem than that of wanting to run scripts after the DOM is created but before images are loaded; we don’t have enough images in my app to worry about.

  • Comment by: Joe Shelby
  • Posted:

For IE, what about using IE’s non-standard onReadyStateChange event? If you hook it up to “document” it will fire and document.readyState will equal “interactive” when the page can be accessed by script. I haven’t found anything that conclusively says this is the same thing as the DOM being loaded, but it behaves the same using your “busted.html” example.

Just add this to the code, it won’t effect browsers that don’t understand it:

/* for IE */
function statechange() {
	if (document.readyState == "interactive")
		init();
}

document.onreadystatechange = statechange;
  • Comment by: David W.
  • Posted:

I’m going to say this one final time:

The readyState property is not a reliable way to determine if the document has been loaded and parsed.

  • Comment by: -dean
  • Posted:

Oops sorry for the repeat. I worked with your test file some and see what you mean.

I notice you also mention that simply calling the init script at the bottom of the page is not 100% reliable either.

So I propose (and have successfully tested in Firefox, IE, and Opera) using a combination of methods. Add this to the bottom of the page (I haven’t found much difference between including it as the very last bit before the close body tag or between the close body and close html tags):

/* for IE */
function statechange() {
	if (document.readyState == "interactive")
		init();
}

if (document.readyState) {	
	if (document.readyState == "interactive" || document.readyState == "complete") {
		init();
	} else {
		document.onreadystatechange = statechange;
	}
}

Called at the end of the document, it will run init if readyState is either “interactive” or “complete”, or create the onreadystatechange event if it hasn’t reached either yet.

As a bonus, Opera recognizes the document.readyState variable even if it doesn’t recognize the onreadystatechange event, so Opera will also be able to init() before having to wait on images to load.

I’ve combined your ‘busted.html’ test with your ‘readyState.html’ content (and tripled the content for extra DOM padding) to stress-test this technique and have met with success. I wish I could test for Safari or Konqueror but I’m on Windows. You can run my test here.

  • Comment by: David W.
  • Posted:

OK. Can people stop posting alternative solutions that involve either the readyState property, <script> tags at the end of the document or combinations thereof. These solutions are not reliable. I will delete any further postings along these lines so don’t waste your time.

  • Comment by: -dean
  • Posted:

Without a solution for Safari this is interesting but still not quite practical IMO. I’m still falling back to a timer that watches for some conditions to become true (e.g document.getElementById(‘footer’).. We can shortcut that by including the event handlers for Gecko and IE – that’s good, but in the interest of less code forks and more simplicity, its easier to treat all browsers as the same. That said, I did come across an interesting problem using this approach. My original test was document.getElementsByTagName(“body”)[0].. reasoning that if the body node exist, my DOM is loaded and ready. Not so, so I’m back to checking for my footer (which makes this only useful where I can rely on knowing the id of the last element in the page.)

Does anyone know a better test that assures DOM readiness x-browser?

  • Comment by: Sam-I-Am
  • Posted:

Wouldn’t the cleaner solution be to add the following to your page:

<script defer="defer" src="onload_fix.js" type="text/javascript"></script>

And then have your onload_fix.js look like this:

// for IE
if( document.all ){
  init();
// for Mozilla browsers
} else if (document.addEventListener) {
  document.addEventListener("DOMContentLoaded", init, null);
// catch everything else
} else {
  window.init();
}

Granted, there’s potentially an issue where the DOM might be ready in Mozilla before it loads this script, but as small as the file is, I’m not sure it would be a huge issue.

I’m using:

// Pre onload code execution.
function checkDOM(){
    if (getById("footer")||getById("credit")) Startup();
    else setTimeout("checkDOM();",100);
}

if (document.addEventListener) {
    document.addEventListener("DOMContentLoaded", Startup, null);
} else {
    setTimeout("checkDOM();",100);    
}

There is a bit of an artifact where the original DOM is displayed, then I make some edits. But it will do for now.

dean, are you sure that deferring requires an external script? So far in my tests, an inline deferred script works just as well. The only difference is that the inline deferred script runs before the onpropertychanges, while the external deferred script runs after it, which means that if there was a “loading” readyState, it would still be “loading” on the inline defer, and “interactive” on the external one. I’m not sure if that means anything. Both can access the very last element in the document (just before the </html>) via id. Do you have a testcase in which the inline deferred script doesn’t work?

  • Comment by: Maian
  • Posted:

So does this work?:

<script defer="defer">
 if( moz )
  document.addEventListener("DOMContentLoaded", init, null) :  
 else
  window.onload = init();
</script>
  • Comment by: Dan Lewis
  • Posted:

Errr… I wish I could edit posts, delete them, and/or show code in them. Then I might be able to properly take part in the conversation without spending ten minutes trying to hack a snippet for HTML escape sequences.

Remove “window.onload =” in the above code. I typically use http headers and file extensions to specify mime-types; so I think it’s triple redundant redundant redundant to specify type and language. :p

  • Comment by: Dan Lewis
  • Posted:

Still got a bunch of Problems with the ‘readyState’-Trigger.

Mozilla: Like mentioned before, Firefox does not know readyState. But somehow the Scipts don’t have to wait for anything. Although i still retrieve the designed parentobject via ‘getElement’ just to make sure, it’s already there.

Opera: Stays interaktiv while the images are loaded. ReadyState is no help breaking the boundary.

Internet-Explorer: Even with readyState equals complete you can get trouble if you want to append something to the body. The reason seems to be, that the body-tag still is not closed, inspite of readyState tells us something else.

Most of my problems seem to be caused in my attempt to append something to the body, while the body is the last thing in the DOM-Tree, which is complete. I rather should ask for the tag is closed, but still have not found a way to do so.

  • Comment by: KrautHead
  • Posted:

Here’s the log of a modified version of dean’s test page:

document.onpropertychange (propertyName=onpropertychange (srcElement=null); last=null; body.offsetHeight=Object required)
document before body (readyState=loading; last=null; body.offsetHeight=Object required)
document body start (readyState=loading; last=null; body.offsetHeight=0)
styleSheet1 applied
styleSheet2 applied
document body half-way (readyState=loading; last=null; body.offsetHeight=12028)
document body end (readyState=loading; last=null; body.offsetHeight=23808)
document after body (readyState=loading; last=[object]; body.offsetHeight=23808)
defer1 (readyState=loading; last=[object]; body.offsetHeight=23808)
document.onreadystatechange (readyState=interactive; last=[object]; body.offsetHeight=23808)
document.onpropertychange (propertyName=activeElement (srcElement=null); last=[object]; body.offsetHeight=23808)
defer2 (readyState=interactive; last=[object]; body.offsetHeight=23808)
image.onload
document.onreadystatechange (readyState=complete; last=[object]; body.offsetHeight=23808)
window.onload
document.onpropertychange (propertyName=activeElement (srcElement=null); last=[object]; body.offsetHeight=23808)

“defer1” is the inline one, while “defer2” is the external one. The deferred scripts are added in the header:

<script type="text/javascript" defer="defer">
  orderOfEvents[i++] = "defer1 (readyState=" + document.readyState + "; last=" + document.getElementById('last') + ")";
</script>
<script type="text/javascript" defer="defer" src="defer_init.js"></script>

The external deferred script calls init() which has the same code as the inline deferred script.

As you can see, the inline deferred script can access the last element (which is a script element right before the closing html tag) and correctly shows document.body.clientHeight. However, it triggers before readyState changes to “interactive” (if it were not “interactive” already) and before the activeElement property change. For most intents and purposes, this is good enough (activeElement is useless at this point).

I’ve also noticed that when going to the page from another page (i.e. not reloading), readyState is already “interactive” skipping the “loading” state. When reloading, readyState is “loading” until after the inline deferred script runs, at which point it becomes “interactive”. So although readyState is useless for detecting when the DOM is fully loaded, this info could be used elsewhere…

  • Comment by: Maian
  • Posted:

Good work Maian. I already made a modified test page with deferred scripts included if people want to take a look:

http://dean.edwards.name/my/readyState2.html

  • Comment by: -dean
  • Posted:

This is odd… When I load http://dean.edwards.name/my/readyState2.html, the deferred inline script executes prematurely. However, when I load of a copy of that page (and the script) onto a server in my company’s LAN, the deferred inline script fires after the document finishes loading.

Here’s my hypothesis: The disparity is caused by the difference between download speed and parsing speed. The parser will parse through the available data, then see if there are any deferred scripts that need executing. On my LAN, the html file is downloaded so quickly that the parser has doesn’t get to check for deferred scripts until after. However, when I download from your site, it’s slow enough that the parser has time to check for deferred scripts.

However, I don’t know why there’s a difference between inline and external deferred scripts. The HTTP request sent for the external deferred script is not delayed – it’s sent before the request to get the image. I also added another external script, this time not deferred, after the deferred external script in the header, and the deferred external script was still requested before the non-deferred external script.

Maybe it’s some server setting? I tried it both on my local server and a server on the LAN. My server runs Tomcat 5.5 on top of Apache2 with mostly default configs. The other server is ISS and I dunno about its settings. In both cases, the deferred inline script executes after the whole document is parsed.

  • Comment by: Maian
  • Posted:

Maian – the point is, as I mentioned in my original post, it is only a reliable method when using an external deferred script. Despite all the arguments and suggestions to the contrary, my original post describes the only reliable way of using this technique. I just hope that people can be bothered to read all the comments!

To reiterate. Ignore all of the comments to my post. My post describes the only reliable technique for determining if the document has been loaded and parsed on IE/Firefox.

  • Comment by: -dean
  • Posted:

In what way needs this window.onload script to be adapted so it can be used to handle multiple functions that all need to be fired with the window.onload event as well?

eg referring to the AddLoadEvent function for multiple load events by Simon Wilson

  • Comment by: Jo
  • Posted:

dean said: To reiterate. Ignore all of the comments to my post. My post describes the only reliable technique for determining if the document has been loaded and parsed on IE/Firefox.

However, this technique doesn’t work too well with Safari. The test page only loads the red banner when the image has finished loading.

It has to do with Safari having the method .addEventListener, but not generating events of type DOMContentLoaded.

I had to stick in a conditional clause testing for Safari (urgh!) in order to get my stuff to load. I’m still testing for what I know is the last object, and that seems to be working pretty well.

function checkDOM(){
__if (document.getElementById(“footer”)) {
____var func = document.ondomload;
____document.ondomload = function(){};
____func();
__} else {
____setTimeout(“checkDOM();”,100);
__}
}
_
setTimeout(“checkDOM();”,100);
if (document.addEventListener)
__document.addEventListener(“DOMContentLoaded”, checkDOM, null);
if (document.readyState)
__document.onreadystatechange = checkDOM;
//add event
document.ondomload = function(){alert(‘hi there’);}

this will just register to any event that might do the trick, but checks it just in case it’s not correct.

  • Comment by: Analgesia
  • Posted:

If this maybe a useful link to this article …
On this page http://www.brothercake.com/site/resources/scripts/domready/ you have a script that use a time-out to check if any DOM script is ready for execution. I found that when using IE WIN (5 and up) and tested a DOM script locally it actually removed the error mesage (JS console) that the element retrieved with the method document.getElementById was undefined.

  • Comment by: Jo
  • Posted:

Interesting discussion. I wanted to post some snippet but I get an XHTML for the script part:(
I ripped it off http://www.junit.org/, function jsUnitSetOnLoad(), made some minor changes so it works w/o junit.js as well.

Have fun,
CirTap

  • Comment by: CirTap
  • Posted:

whoops, sorry — wrong URL … http://www.jsunit.net/

  • Comment by: CirTap
  • Posted:

[…] user will have to wait for the page to be fully loaded before the page becomes useful. A solution to this vexing issue is posted by Dean Edwards. […]

[…] AJAX Programming | Dean Edwards proposes a solution to window.onload problems here. […]

If you’re gonna use a script like this it may not be appropriate to fall back on window.onload – it certainly isn’t appropriate to do that by defualt – because these are two entirely separate points in time, and a script designed to work with one point won’t necessarily work with the other.

For example – a function bound to window.onload can know that any other object on the page has loaded its data as well; so something like an HTML document embedded with an <object> can be used as a data source without having to monitor its load event separately. With a function that fires before onload, this is not the case.

Who has the “initFunctions” array, where every function that needs to get initialized gets pushed onto that array, then the final onload simply iterates through the functions=

I tried it by myself, but didn´t get it to work. Please help.

  • Comment by: aceman3000
  • Posted:

Any special reason why the done property is set on the arguments.callee?

Why not just

init.done = true;

  • Comment by: José Jeria
  • Posted:

José – arguments.callee is more generic. If the init function were renamed (or was anonymous) then we would have to change the code.

  • Comment by: -dean
  • Posted:

Excellent. I applied the “defer” bit to some code for fixing PNG transparency in IE (and also replaced the onload-bit with calling the function immediately).

The result is PNG transparency in IE without any brief initial moment of non-transparency.

  • Comment by: Henrik Nyh
  • Posted:

[…] read about this on DMM. It’s a nifty idea to use various obscure features to run an onload after the DOM&#821 […]

[…] ment, what is so inconvenient about “hard coding” an onload event handler? However, “The window.onload Problem—Solved!” still rejoices. PHP 5 in Depth I really had serious prob […]

People are writing here for XHTML the defer attribute should be defer=”defer”. But Microsoft’s API documentation states that the value is boolean, and can accept either “true” or “false”. I have no idea whether “defer” will be interpreted as either “true” or “false”. So I would use the non-value (attribute present) non-XHTML style (as in the original posting) or I would use “false” (right?).

MSDN docs on script defer (search “script defer” if the ever-rotting MS links are busted for you).

In any case, I got odd “Illegal Character (Line 2, Character 1)” errors on the HTML file from IE when I used this method, after introducing some CSS to the file via a link element. I had incorporated the conditional compiling instruction into a .js file rather than directly into the HTML file (I would just use the conditional comment version in that case). It was fine without the CSS; the CSS was 100% vanilla (it used some background image URLs is the only remotely advanced part about it).

The conditional comment version fired the script before even the background images loaded for the CSS, which gave an unpleasant hysteresis. So I went to the HTC method and the error and lag went away (perhaps because of CSS loading order). I set media=”screen” to avoid print errors on the HTC’s style element.

I’m too tahred at the moment to provide you with reproducible test cases &c but I thought you might like these reports from the field…thanks very much for sharing these secrets.

  • Comment by: peeder
  • Posted:

Maybe I don’t quite get it.

Tested both the original and htc versions with my problem page(s).

The original executes the script on all browsers I tried(IE6,Firefox1.0.7, Opera(8.5 and 6.01 (original example waits for page to fully load)),Netscapes 8, 7, and 4.5).

Could only get htc version to work with IE6.

But here’s the real problem for me:

I don’t have ANY images or binary content, just a lot of text (256kb of chemicals).

Behavior is identical with the "solution" as when I use <body onload=…>: it waits until ALL text is loaded.

Anyone have a clue as to how I can get the script running prior to loading?

  • Comment by: mash
  • Posted:

I looked at the code and it really works ok. However the thing with winIE did worry me so I altered it: – You don’t have to use an external file for winIE anymore. Instead I used DOM methods to create the javascript. I couldn’t get the browser class in (browser.winIE) in this post because this post system won’t let me but you can imagine how to check for browser.winIE

Look at the code:


  function setupDomLoader(){
    if (document.addEventListener) {
      document.addEventListener("DOMContentLoaded", domLoader, null);
    }
    if (browser.winIE){
      var head = document.getElementsByTagName('HEAD')[0];
      var script = document.createElement('script');
      script.setAttribute('defer','defer');
      script.appendChild(document.createTextNode(domLoader()));
      head.appendChild(script);
    }
    window.onload = domLoader;
  };
  setupDomLoader();

  function domLoader(){
    if (arguments.callee.done){
      return;
    }
    arguments.callee.done = true;

    alert('loaded');
  };

grt, Mark

  • Comment by: Mark D
  • Posted:

Mark – setting the defer attribute does not work. I mention this in my original post.

  • Comment by: -dean
  • Posted:

webgraphics: Dave: Inline JavaScript: What’s the Problem? Dave:I have always been under the impression that inline JavaScript is a Bad Thing, presumably because it violates the whole separation-of-content-and-behavior idiom. I typically do what (I assume) most other people do in their scripts. That is, I bind an

  • Trackback by: ECTIO
  • Posted:

When are document elements available to scripts? As far as I can tell, the DOM and HTML specifications are silient on the issue of when DOM elements become available to scripts embedded within the document. That is, if you have an element <div id=”foo”>, when can…

[…] Knowing more about this could lead to some nice performance optimisations. Dave points to Dean Edwards’ neat hacks, which are very useful if browser-dependent. Dave’s seeking […]

I just put all my JS at the bottom the page, then you can see everything above it. Am I a bad man?

  • Comment by: David T
  • Posted:

[…] so used Neil Crosby’s PHP script for serving appropriate MIME types. I also followed Dean Edwards article that shows you how to overcome the lack of support for a common ‘onloa […]

Automated Google Analytics download tracking

What about a single initializing-script ?

<script defer src=”init.js”></script>

init.js will contain helper-code for Mozilla/Other browsers and simply run init() in case of ie.

  • Comment by: Ilia
  • Posted:

Getting this to work in Safari involves an involved object detect in the if(document.addEventListener) code:

if(document.addEventListener &&
  !(!document.all && document.childNodes && !navigator.taintEnabled)){
	document.addEventListener('DOMContentLoaded', domInit, null);
}
else{
	addEvent(document, 'load', domInit);
}

An alternative would be to specifically detect Mozilla / Firefox to the exclusion of others, which would probably be as nasty.

  • Comment by: Phil Green
  • Posted:

Additional: Just discovered that this doesn’t suit Opera 8.51 (grr), so another check is required:

if(document.addEventListener && !window.opera &&
  !(!document.all && document.childNodes & & !navigator.taintEnabled))
  • Comment by: Phil Green
  • Posted:

XInclude in Javascript using XMLHttpRequest It currently only works in Mozilla, but I just threw together a quick, non-comprehensive implementation of the XInclude specification in JavaScript. I decided to use the DOMContentLoaded event (thank Dean Edwards) so it will load before images. It sup

Thanks Dean, I’ve been having trouble passing variables to a flash applet (in Firefox only) and have been resorting to setTimeout() functions for waiting 200 msecs before passing the values from JavaScript. This trick provides better performance for many applets.

Ok.. I saw this and I was intrigued, but then I started thinking, why are you bothering with this. If you have already gone far enough to disregard accessibility by using DOM scripting why not just build the entire page with your scripts. That way there is no Images to wait for, your script should execute right away. You can start build up the page imediately and not have to worry about flaky methods of learning when the page is loaded.

Am I crazy here?

  • Comment by: Paul
  • Posted:

Paul – ‘disregard accessibility by using DOM scripting’? When written properly (and thoughtfully), DOM scripting will only provide enchancements to the browsing experience, not hinder accessibility of the content itself. That’s what graceful degradation is all about.

Don’t dismiss a technology purely because of it’s potential for misuse.

  • Comment by: Cameron
  • Posted:

Agreed. That is exactly what I’ve used this for; a Bobby AAA compliant page that has scrolling news should you be running JavaScript without any flicker as it gets implemented from the original content, or a nice unordered list if you aren’t (or aren’t running IE>=5 [Win], Mozilla, Safari, Opera 8, Camino etc., hell those users don’t even get a stylesheet).

What annoys me is people who deliberately cripple a browser in a way that doesn’t reflect real-world usage and then bleat “I couldn’t use your site, you’re not accessible” when the real audience have no problem. And they’re everywhere, claiming to be an “accessibility advocate”, which they aren’t. That would be me;)

  • Comment by: Phil Green
  • Posted:

I haven’t put this through the rounds of x-browser testing yet, but it looks good so far. The solution could be extended by implementing an Observer Pattern:


window.onloadListeners = new Array();

window.addOnLoadListener = function(listener) {
	window.onloadListeners[window.onloadListeners.length]=listener;
}

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;

       // iterates through array of registered functions 
       for (var i=0; i<window.onloadListeners.length; i++) {

		var func = window.onloadListeners[i];
		func();
	}
 };

that way javascript includes, etc could register themselves using:

window.addOnLoadListener(YourFunctionName);
  • Comment by: Ian Darke
  • Posted:

Google Analytics Downtime Google Analytics was down for a few hours today. While it is not unusual for an ASP service to have…

[…] The first problem we can tackle by applying my onload hack. I’ll use the example of Ben Nolan’s Behaviour: Behaviour._apply = […]

[…] lt;v .length;i++){ var el=v[i]; el.outerHTML=el.outerHTML } } function init() { // Found via: http://dean.edwards.name/weblog/2005/09/busted/ // Used to call rewriteContent as soon as the DOM has loaded. // quit if this function has already b […]

Kinda hacky, but worth every bit. This rocks.

Thanks Dean!

Dude. I’m a hacky kinda guy.

  • Comment by: -dean
  • Posted:

You can’t depend on document.addEventListener to find moz, since it exists in other browsers. Try this:

if ( window.controllers ) document.addEventListener(‘DOMContentLoaded’, initMain, false); } ….

Thanks ydnar!

By the way, your email checking is busted, plus is allowed in emails: http://en.wikipedia.org/wiki/Email_address#Plus_addressing

is there something wrong with:

var addLoadEvent = function(func){
if (window.addEventListener) {
  window.addEventListener('load', func, false);
} else {
  window.attachEvent('onload', func);
}

this works in every browser I’ve tested it in.
And, if nothing else, you can fallback to something like:

if (window.onload) {
  var func = window.onload;
  window.onload = function() {
    whatever here
    func();
  }
}

@David – we don’t need to detect Mozilla specifically. If DOMContentLoaded is not supported then nothing will happen and we will revert to the original onload event.

@Jeremy – You seem to have completely missed the point of this solution.

@Everyone – I’m closing comments on this post now. The later comments are at best pointless and at worst misleading. I’m not going to waste my precious bandwidth uploading more of the same.

  • Comment by: -dean
  • Posted:

[…] vered its a much discussed topic, specially in Dean Edward’s blog, where an alternative to DOMContentLoaded for IE has als […]

The window.onload Problem Revisited Today I was researching ways to provide Mozilla’s DOMContentLoaded functionality in Internet Explorer. Dean Edwards has already demonstrated two ways that this can be done, using the script “defer” trick and behaviors. Both methods require that an e…

[…] This is a follow-up to my original window.onload solution. […]

[…] Window.onload entistä paremmin Dean Edwards esittelee pari parannusta alkuperäiseen ratkaisuunsa onload-ongelmaan: tuen Safarille ja erllisen IE:tä varten tarvittavan tiedoston ta […]

[…] el=”tag” title=”View the technorati tag: Editorial”> Dean Edwards worked on a document.onload solution back in the day. He has just updated it with new information that fixes […]

[…] I’ve combined a script and some fixes to make things work on my site: The Visibility Toggle – http://www.interactionarchitect.com onLoad Fixes – Dean Edwards […]

Google Analytics Downtime Solutions Google Analytics was down for a few hours today. While it is not unusual for an ASP service to have downtime,

[…] Dean Edwards a publié en Septembre 2005 une première solution (valide pour Mozilla et IE) qui permet de lancer ses javascripts plus tôt que sur l’évènement window.onload qui ne survient que lorsque 100% des éléments de la page sont chargés. […]

[…] a while back i was following the window.onload debacle and […]

[…] Now, I wondered how to make that better. A simple solution would be not to use onload on the window (and thus waiting for the document and all dependencies to be loaded before you proceed) but use onAvailable or equivalent solutions on the element itself instead. However, if the element to highlight is not the first, that would still make it possible to enter other data until the focus gets taken away from you. […]

[…] http://dean.edwards.name/weblog/2005/09/busted […]

re: Google maps crash IE

[…] Dean Edwards has found a solution: his approach uses […]

[…] Voici une nouvelle version de la technique JavaScript DOMContentLoaded sur Ajaxian. Andrea Giammarchi has taken the work of Dean Edwards, Mark Wubben, and Paul Sowden to create his DOMContentLoaded Final Solution. […]

[…] Contrarian Peter Michaux thinks there is still a window.onload problem, despite previous declarations of victory by Dean Edwards. By way of background, Peter’s article begins with the need for a cross-browser DOMContentLoaded event before embarking on a historical tour of attempted solutions. Finally, Peter arrives at the increasingly popular DOM polling technique as the best solution. Great article. […]

[…] After that, I thought briefly that part of the reason the page controls were taking so long to become available was that the onload event was being delayed due to images loading on my page, thereby causing an overall delay in total page-load time. And I found this nifty method to kick off window.onload after the DOM has loaded, but before other things like images finish loading. […]

[…] I wouldn’t want to bore you with more code (in other words, too lazy to type further). For the rest of the browsers and more explanation, you can learn more from here. […]

[…] Yahoo! (well, Dean Edwards did first with their latest YUI release. […]

[…] Dean Edwards addressed this problem for Internet Explorer and Mozilla/Firefox, and later included an update for embedded JavaScript (from Matthias Miller) and Safari (from John Resig’s jQuery). […]

[…] たとえば、Firefox など Mozilla 系のブラウザでは onDOMContentLoaded というイベントがあって、これはそのものずばり、DOM ツリーが読み込み完了したときに通知してくれるイベント 。そして、Safari では document.readyState が、IE では defer 属性を有効にした script 要 の読み込みが利用できる(このへんの詳細は Dean Edwards: The window.onload Problem – Solved! に詳しい)。 […]

[…] 由于上午的Gmap没有调试成功,一直比较郁闷。 1、下午的时候收到K_Reverter的邮件他告诉我可以用window.onload=func的方法来代替Body.onload使用。 2、上网找了点资料,在360doc看到了一个老外关于window.onload的代 ,也拿过来用下。 修改代 如下: <script type=”text/javascript” src=”http://maps.google.com/maps?file=api&v=2&key=ABQIAAAA43IALmfadaZgVb1L-IEizRRKyzQqj4F9myd02mWX0DutlkqY_BQi03TATJA59RMvJYUa4fN7lK7X5g”&gt; </script> […]

[…] Most of the problems with the jail element stem from attempting to encode security contexts in the markup. So, how about adding a header that describes the needs of the site author? That’s the approach taken by Gervase Markham’s Content Restrictions proposal. It looks like a slam-dunk at first. Elite hackers are producing slides about it, and advocating it at conferences. It turns out the security context it provides is a little too coarse-grained. Site authors like scripts, and they like to put them everywhere. The Content Restrictions proposal requires filtering all event handlers in its restricted script modes. Bummer. Of course, authors can still add event handlers at load time, and even advocate exorcising the demons of inline event calls. Would web authors actually use this? Maybe so. It will suck less in Firefox 3, because we have getElementsByClassName. We could also add an API that would let authors set a bunch of event handlers in one native call, if performance is still a problem. […]

[…] Konkret: es gibt natürlich verschiedene Lösungsansätze für das Problem, jQuery bedient sich der Methode von Dean Edwards für ihr eigentlich wunderbares jQuery.ready(). Nur, es funktioniert nicht immer. Und: was geht, und was nicht, hängt zu gut 60% von äußeren Umständen ab, also vom Browser, zusätzlich geladenen Skripten auf die man keinen Einfluss hat (Werbung) und wie schon gesagt, auch gerne mal einem Browserplugin. Und manchmal kommt auch ein ambitionierter Kollege und überschreibt alles mit einem fröhlichem onload. […]

[JRA-13459] Significantly improve perceptual page load times by moving Javascript to end of HTML Short version: After moving Javascript references to the end of the page templates, we saw initial page render drop from approximately 12 seconds to just over 1. Long version: Like many people, we’re currently using Jira over HTTPS. This means (…

  • Trackback by: JIRA: JIRA
  • Posted:

[…] 参考链接如下(其实是前后相继的三篇不断补充的文 ): http://dean.edwards.name/weblog/2005/02/order-of-events/ http://dean.edwards.name/weblog/2005/09/busted/ http://dean.edwards.name/weblog/2006/06/again/ 也可以看看这个: http://tanny.ica.com/ica/tko/tkoblog.nsf/dx/domcontentloaded-event-for-browsers […]

[…] Inspiration and information to this solution has been taken from the following sources: Dean Edward’s blog (window.onload Problem – Solved!, follow up and window.onload – An Alternative Solution) and Diego Perini’s site (ContentLoaded Test Case). MSDN was also useful on background information relating to HTC and behavior. […]

[…] Dean Edward’s post “The window.onload Problem – Solved!” was also influencial in helping me test the script, if you haven’t already read this post I would recommend it. […]

[…] The window.onload Problem – Solved! […]

[…] (6 en 7) hebben daar blijkbaar geen kant-en-klare functie voor, maar op het internet vind je daar wel oplossingen […]

[…] following is a combination of several posts on Dean Edwards’ blog as well as an onload handler for those browsers that don’t […]

[…] but since you’ve got javascript that preloads images, my first thought would be that it’s an onLoad problem. Second (and again, I don’t even know what problem you’re talking about, since it works for me), […]

[…] a lot of cases, this takes far too long and needs to happen a lot earlier. Many clever web developers are tackling this issue and every so often a new answer to end the quest for […]

[…] oggi ho scoperto (tardi???) che con Firefox 3 non funziona. Sono quindi corso ai ripari, trovando la soluzione per risolvere il […]

[…] Henrik Nyh for suggesting using the defer keyword instead of onload as per Dean Edwards’ blog […]

[…] Script Defer by Dean Edwards […]

[…] копаясь нашел тут вот такой вот хак для решения этой проблемы этой […]

[…] stuff. You might want to have a look at this. There's a very quick flash where the colour images are shown because of the way in which […]

[…] 参见: http://dean.edwards.name/weblog/2005/09/busted/ […]

[…] end of the code. This method (which is part of EventHelpers.js, included with the archive below) implements Dean Edwards’ window.onload alternative which doesn’t wait for all the object…. I use this implementation to execute TypeHelpers.insertClasses() when the page loads so any […]

[…] using doesn’t support the DOM ready event, or you don’t know what that means (read this article), you can always simply use the following code to achieve the same […]

Comments are closed.