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

window.onload (again)

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

The negative points of that solution are:

Now, thanks to Matthias Miller, we have a solution for Internet Explorer which does not rely on external files:

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

I’ve been hanging out on the jQuery mailing list lately (very busy, lots of chat). According to jQuery’s creator, John Resig, there is also a solution for Safari!

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

It looks like jQuery will be the first library out there with a genuine solution to this problem.

Why Is This Important?

For those who think that this is not a real issue, consider the following scenario. You have a cute web app called www.TickleMyKittens.com. Of course, it uses Ajax, Comet and Domestos to provide a super-slick interface, this is 2006 after all. But your site is full of kitty images many of which are hosted on a server beyond your control. Your interface is not active until all of those kitties have downloaded. You try to drag a kitty to the litter but she’s just not moving. The page has loaded, what gives? That my friend, is the window.onload problem. The onload event waits for all binary content to download before firing. No kitty-tickilng until then.

I’ll leave it to Dan Webb to have the final word:

Good skills!! That was beginning to worry me a bit. The document ready thing is the linch pin of all this unobtrusive scripting action and without a working version we’d have to give in to Thomas Fuchs and the inline JavaScript crowd.:)

Dan Webb

Update. Opera9 supports DOMContentLoaded! See the comments below.

Comments (229)

Leave a comment

Is there a nice Dean Edwards Seal of Approval wrapper fucntion for all the onload code per chance?

Also your are using document.write to add script to the page – I just wanted to point out that when I used to use Norton Internet Security (or whatever) a few years back it wont let that happen – what I found you had to do was to split the word ‘script’ into at least 2 bits and join it back together like:

‘scr ‘ + ‘ipt’

Just in case people have problems still – I haven’t used Norton/Symantec for years now:D

  • Comment by: Jon B
  • Posted:

I second that. Please give us one of those great will-always-work-anywhere-wrapper-functions:-)

  • Comment by: Andi
  • Posted:

If you absolutely positively have to sniff for Safari, don’t sniff for “safari” – sniff for “WebKit”, the name of the underlying engine and one which propagates into other browsers or browser views in other apps using the same control.

(By the way – your comment form doesn’t recognize that + is a valid character in email addresses.)

  • Comment by: Jesper
  • Posted:

@Andi/Jon – OK. I’ll post a complete solution later (maybe tomorrow).

@Jesper – I’ve changed the sample code to sniff for “WebKit”. What’s the definitive way to sniff for Safari? And, yeah I know about the “+” in email addresses. I keep meaning to upgrade to WordPress 2.0. If that doesn’t fix it then I’ll fix it myself.

  • Comment by: -dean
  • Posted:

Here is a complete cross-browser solution:

// Dean Edwards/Matthias Miller/John Resig

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

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

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

	// do stuff
};

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

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

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

/* for other browsers */
window.onload = init;

And the obligatory sample page of course.

  • Comment by: -dean
  • Posted:

[…] Satunnainen Björklund 15.6.2006 Window.onload Dean Edwards esittelee pari parannusta alkuperäiseen ratkaisuunsa onload-ongelmaan: tuen Safarille ja erllisen IE:tä v […]

Wow! That was fast:-)

Just one question: does this need to be included in the document’s head section or would it also work from an external script file?

  • Comment by: Andi
  • Posted:

Andi: It’ll work in either. I’ve been using it (built into jQuery) from an external file and it works just fine.

  • Comment by: John Resig
  • Posted:

[…] June 16th, 2006 Tags: No Tags. Dean Edwards: window.onload (again) 0 Responses to “Dean Ed […]

By the way: Opera 9 supports “DOMContentLoaded”.

  • Comment by: Oswald
  • Posted:

Oswald: Really? That would make the entire picture complete, then. Every browser would be covered, in some form. I’m going to do some testing on it…

Ok, testing completed, the run down:
Opera 8.5: Failed
Opera 9.0b1: Failed
Opera 9.0b2: Passed

It’s brand new in the latest beta, which I originally didn’t realize.

Thanks a ton for this tip. jQuery now has this change fully integrated into the latest SVN copy. This means that there is now a DOM Ready solution for every major browser. Excellent.

  • Comment by: John Resig
  • Posted:

I must change

if (document.addEventListener) 

to

if (document.addEventListener && !/Opera/i.test(navigator.userAgent))

for use window.onload in Opera(Opera 8.51 seems not support DOMContentLoaded).

[…] rized The DOMContentLoaded, a.k.a. domload, a.k.a. window.onload problem gets solved more and more. There’s now a solution for Internet Explorer that doesn’t r […]

Heh!:)

Anyway, nice to see that there finally is a cross-browser solution for this.

@kentaromiura – you don’t need to sniff for Opera. If it doesn’t support DOMContentLoaded then it will just ignore the event handler.

@Thomas – We all love script.aculo.us really.;-)

  • Comment by: -dean
  • Posted:

Yeah, I was only joking.

I’m really pleased about this though. It’s a good thing and hopefully will give more weight to unobtrusive scripting in general.

  • Comment by: Dan Webb
  • Posted:

Apparently, there’s some doubt over the correct implementation of defer. So it might be worth checking that this is running on a version Internet Explorer where it’s known to work. Then, if Microsoft has got it wrong and, being model internet citizens, fix it in a new version of IE, the script won’t blow up.

  • Comment by: Daniel
  • Posted:

Dean, why the change of the init function and the sniff for Safari from the post in the original article?

Maybe I’m being nitpicky, but why not use addEventListener to add the onload event right after the DOMContentLoaded part (to avoid clashing with any other script that adds an onload event that way), and also use a series of if/else’s in the script, where the last else is a fallback to the window.onload portion. It’s probably unnecessary, but it seems cleaner that way (at least in my mind).

@Daniel – This has been tested to death. Don’t worry.

@Brian – The code is mostly illustrative. Developers can use whatever fallback technique they like. I use window.onload because everyone understands it.

  • Comment by: -dean
  • Posted:

Dean, I’m worried about how the script will work with IE8/9/etc. Maybe a little over-paranoid, I admit.

  • Comment by: Daniel
  • Posted:

Much appreciated!

  • Comment by: Martin
  • Posted:

May we safely assume that the Safari solution will also work for Konqueror and anything else that uses KHTML? If so, should we test the user agent string for “KHTML,” rather than “WebKit”?

Now thats cool:) I’ve extended the prototype library to utilise it: http://agileweb.org/articles/2006/06/16/speeding-up-onload

I tweaked the IE part – so that it uses a timer, which makes it easy to have multiple onloads.

  • Comment by: Ross
  • Posted:

@Andrew – Assume nothing. Test it!:-)

  • Comment by: -dean
  • Posted:

@Andrew: doesn’t work in Konqueror.

@Christof: It does work in Konqueror 3.5… Tested with RUZEE.Events, which basically implements all the methods, plus some ugly-hack to support Opera 8.5, in an easy to use lib.

  • Comment by: Steffen
  • Posted:

[…] thentication with HTML forms Nice hack. (tags: http auth javascript rest XMLHttpRequest) window.onload (again) Dean Edwards etc. solve the onload problem. Sweet. (tags: hack web javascript […]

So I took this and refactored it into an Object Literal. I added the ability to pass any function (rather than just “init()”) into it. So you should be able to just drop that into a page and make the call.

I don’t have every browser to test on here at home, so please let me know if there’s any errors.

  • Comment by: Rob Cherny
  • Posted:

(In the words of Anne, which you surely remember)

Oh, and Dean Edwards, please don’t use null there, okay?;-)

  • Comment by: Mislav
  • Posted:

@Rob Cherny: is it possible to call “DomLoaded.load” several times with different functions? For example:

DomLoaded.load(function() {
    alert('foo');
});
DomLoaded.load(function() {
    alert('bar');
});
  • Comment by: Tony
  • Posted:

Hi Dean nice to see you are still working hard on these issues…

This is what I have been using for a while (original idea from brothercake) but with something new that make it work everywhere, no browser sniffing (I know you like it), no events used:

var onDOMLoaded=function(f,t){
    if(typeof document.getElementsByTagName!='undefined' && (document.getElementsByTagName('body')[0]!=null || document.body!=null)){
        var h=document.getElementsByTagName('HTML')[0];
        var s=document.createElement('script');
        s.type='text/javascript';
        h.appendChild(s);
        s.text=f+'()';
    }else if(100>t){
        setTimeout(function(){onDOMLoaded(f,t)},t);
        if(10>t){t++;}else{t+=10;}
    }
};

Actually, the “busted3.html” test does not fire any event if viewed from your site. The problem disappears if I download the example locally on my disk or on my server. I am using Firefox 1.5.0.4 Linux.

The onDOMLoaded snippet is tricky and the order is critical in Opera, especially the script text must be injected after the script object have been appended to prevent a double call to the callback function. If somebody have native Safari please test it…

Hope you can use and like it as I do, since it does the JOB…

Ooops…onDOMLoaded instructions…

To call it you need to pass as arguments the function name (String) and a 0 (Int).

   onDOMLoaded('init', 0);

The second parameter ‘0’ is to initialize a counter, and I used the function name instead of a reference to avoid making a copy of the init() function, this uses less space in the Globals.

If you dont like change the line:

   s.text=f+'()';

with:

   s.text='('+f+')()';

and pass a function reference to onDOMLoaded.

That’s all…

[…] Appalling. A list of the 100 most inspiring films. … window.onload (again)For Internet Explorer you need an external JavaScript file; We only had genui […]

@Tony: No, the second call overrides the first as DomLoaded is essentially a variable (in this case an object) and you’ve overrided the value with the second call.

In the case of your example, it’ll only fire the second one because it’s executed after the values are set, and the “bar” was the most recent setting.

Hope that helps.

  • Comment by: Rob Cherny
  • Posted:

@Mislav – Good spot! Thanks.:-)

  • Comment by: -dean
  • Posted:

@Tony: Hmmm … it might be kinda neat and handy to add that as a feature though, so you could line up function calls for the DOM loading…

Hmmm…

  • Comment by: Rob Cherny
  • Posted:

Thought it might look more like this??

  • Comment by: Lucky
  • Posted:
js> var x = 0; delete x
false
js> y = 0; delete y
true
  • Comment by: albert
  • Posted:

[…] usuario se enfrentará a controles que no responden. Dean Edwards nos da buenas nuevas en window.onload (again). Esta solución ya se ha incorporado a la versión SVN de jQue […]

The solution presented in #31/#32 works equally well in all browsers…it would be great (really) if somebody spotted known problems on it’s use.

I “believe” that the trick which make it work stay in the fact that a SCRIPT element is appended at the end of the document (to the HTML element to be exact) just after the BODY element have been loaded (busywaiting… thanks goes to brothercake for this).

So first I wait for the BODY to load, then instead of calling the init() function a SCRIPT is appended to the HTML element and this in turn will call the init() function once the DOM has been completely parsed.

It seems to me this behavior is something similar to the DEFER attribute for IE, emulated on all browsers/platform with no use of events, just a small cpu load at the very beginning due to fast setTimeout.

The comments on this thread where so enlightning…thanks.

@Dean: There where no error in your “busted3.html”. Just a stupidity on my part saving the result page with Firefox 1.5.0.4 which resulted in a page with elements already added by the script.
Hope this will help others not doing the same mistake.

I put together a version that allows you to attach multiple events, borrowing from Dan Webb’s Prototype extension and Simon Willison’s addLoadEvent:

http://www.thefutureoftheweb.com/blog/2006/6/adddomloadevent

I’ve also combined this with the work of Simon Willison, to produce a script that allows you to use both DOMLoaded and window.onload. This was needed, as some of the stuff needed to be run after binary content was loaded.

http://roderick.dk/javascript/init-handler/init-test.html

this solution has been patched into Prototype as well 8P http://www.agileweb.org/articles/2006/06/16/speeding-up-onload

  • Comment by: Mario
  • Posted:

How closely do other people watch memory leaks? It looks like this example leaks a “script” element on every page load, which could be avoided by clearing the “script” variable.

@Matthias – which example leaks?

  • Comment by: -dean
  • Posted:

Dean and other webdevvers (#11, #14),

please don’t consider your script 100% cross-browser if it doesn’t work in Konqueror. I know you need to draw a line somewhere, but I think saying KDE’s own Konqueror is less important since there is Mozilla and Opera for Linux is like saying Apple’s own Safari is less important since there is IE for Mac.

Scroll back to #22, #24, #25 and #26. This will work in Konqueror (tested with Konqueror 3.2.2 on Knoppix+KDE), just change your WebKit sniff to KHTML.

I can’t seem to time my screencapture smooth enough to present you a printscreen of the airplane picture half loaded and the text already printed, but just trust me on this one.

  • Comment by: Herman Scheele
  • Posted:

This solution has been patched in the DOMContentLoaded addevent handler in events.js at DOMContentLoaded for Browsers, Part II, thank you.

Herman,

My version does work with Konqueror, please see the DOMContentLoaded addevent sample page on BrowserCam.

Dean, this may look something that has nothing to do with DOMLoaded, but I believe we will need this to see when we have gotten all the elements we where expecting, then fire the event.

function getTail(url) {
        var r = null;
        try { r = new XMLHttpRequest(); }
        catch(e) { r = new ActiveXObject((navigator.userAgent.toLowerCase().indexOf('msie 5') != -1) ? 'Microsoft.XMLHTTP' : 'Msxml2.XMLHTTP'); }
        r.open('GET', url, false);
        r.setRequestHeader('Range', 'bytes=-512');
        r.send(null);
        return r.responseText;
}

I already tested this in several browsers with very good results. Matthias Miller suggested a tricky test case with delays in between PHP output as a ‘good proof’ if that will work or not. I was not aware of this until I saw the problem with my previous version of onDOMLoaded.

Working examples of this new method can be found here:

http://javascript.nwbox.com/onDOMLoaded/

This getTail() is not yet implemented, I am still using HEAD Requests or GET if a PHP to know the URL final size and than compare with the incoming…

Please comment on this, especially if you see some drawbacks in using this method through XMLHttpRequest.

[…] t seen Dean’s post, they did it and it is good shit. If you want to read more of an explanation head over to Dean’s site. I’ve been waiting for, but in a very idle way, […]

Was wondering, can’t this be done without the conditional comments?

new function() {
  var w = window, s = "DOMContentLoaded", b;
  var d = document, f = function(e) {if (!b) {/* your code here */} b = true};
  if (d.addEventListener) {
    d.addEventListener(s, f, false);
    w.addEventListener("load", f, false);
  } else if (d.attachEvent) {
    w.attachEvent("load", f, false);
    d.write("<script id=__ie_onload defer src=javascript:void(0)></script>");
    d.getElementById("__ie_onload").onreadystatechange = function(e) {
      if (this.readyState == "complete") f();
    };
  } else if (/WebKit/i.test(navigator.userAgent)) {
    var i = setInterval(function() {
      if (/loaded|complete/.test(d.readyState)) {
        clearInterval(i);
        f();
      }
    }, 69);
  }
};

I don’t have every browser known to man, but it works great on FX1.5 & IE6??

  • Comment by: Lucky
  • Posted:

@Lucky -All you’ve done is swap one browser sniff for another. Sniffing for attachEvent is not a good idea as Opera supports this alternative to addEventListener.

  • Comment by: -dean
  • Posted:

This doesn’t work so well for IE over https. the script’s src=javascript:void(0) is considered by IE to be non-secure content, so you get a popup dialog saying the page has both secure and nonsecure data. A little problematic. Is there an alternative to using javascript:void(0)? Otherwise, it seems like adding a call to your init method at the end of the document is the only viable solution.

  • Comment by: Rob
  • Posted:

@Rob – Thanks for the info. If you are using this technique over https then use the original solution.

  • Comment by: -dean
  • Posted:

@dean – but it tests for addEventListener first .. else if attachEvent .. so wouldn’t Opera work under the first condition?

and was just curious the conditional-comments are fine, just wondered what the smallest footprint could be.?

  • Comment by: Lucky
  • Posted:

@Dean / @Tanny,
please pay attention on how you use the “onload” method mixed with the ‘DOMContentLoaded’ method. You should make the last line of your example conditional to “DOMContentLoaded” and not always include it.

What happend in the code you are talking about is that IF THERE ARE DELAYS or SLOW LINKS, or simply a somewhat BIG PAGE the “onload” is called before “DOMContentLoaded” so since you look if the init() function has already fired with:

if (arguments.callee.done) return;

the real benefit of the “DOMLoaded” is cancelled… init() will not be called at the right time, at that time you will have much less elements available in your DOM.

We are trying to add capabilities to browsers that does not have them, not obfuscating capabilities of browsers that have them already working…grin!

I have a test case for what I am saying. And yes I know it is a painfull job we have…

I suggest that as a debug facility to test your findings you do:

alert((document.all || document.getElementsByTagName('*')).length);
setTimeout("alert(document.all || document.getElementsByTagName('*').length));", 1000);

When you “believe” the DOM is ready/complete.

You should get two alert() boxes with the same elements count.

As Matthias Miller said, if you do not insert delays in between outputs the test case may say it works when it realy doesn’t. So test cases must be some PHP.

Do you intend to make this work with dynamic pages or just static HTML ?

For https use what devs use for the iframe issue (instread of src=javascript:void(0); use src=javascript:false;

no need to go back to the old method of external files…

  • Comment by: Mario
  • Posted:

In cases where I’ve need to launch a function after the DOM had loaded and prior to loading all of the images and other site gunk, I’ve just inserted a transparent pixel and attached an onLoad event to it.

While this method is admittedly more a kluge than a clean programming solution (heck I’m a marketer), it is pretty darn straightforward, doesn’t add a bunch of programming overhead, and adds in flexability (e.g. you can load half the images before launching the onLoad event).

  • Comment by: dan
  • Posted:

If it must fire as soon as possible after the DOM has started importing and we don’t want to replicate exactly DOMContentLoaded waiting the very last element I would like you (Dean) look at this again which was posted as #31/#32, now Konqueror works and I made some minor tweaks:

var onDOMLoaded = function(w, n) {
    var d=w.document;
    if (typeof n == 'undefined') { n = 0; }
    if (d.body && (d.all || d.getElementsByTagName('*')).length > 2) {
         // swap the comments on the following two lines if you
         // want to see a count of elements @ DOMLoaded time
//      var f='var cc=document.all || document.getElementsByTagName("*");alert(cc.length);';
        var f='init()';
        var h = d.getElementsByTagName('html')[0];
        var s = d.createElement('script');
        s.type='text/javascript';
        if (/KHTML/i.test(navigator.userAgent)) {
            s.innerHTML=f;
            h.appendChild(s);
        }else{
            h.appendChild(s);
            s.text=f;
        }
    } else if(1000 > n) {
        setTimeout(function() { onDOMLoaded(w, n); }, n);
        if (10 > n) { n++; } else { n += 10; }
    }
};

– it does not use events in any browser (the existing one will still work)
– minimal browser detection is used for Konqueror only (append order)
– no global variables, no function calls, no closures only a busy loop
– does not use document.write, I prefer the DOM way while at it
– use setTimeout because at every step timeout is changed

Call it in each window/iframe you may need in this way:

onDOMLoaded(window);

Higher timeout steps more elements you will have when init() is called, this is nearly perfect in a static HTML environment, it may make some difference in a dynamic (PHP, Python, Perl) environment. Use the one that fits you.

Seems to me it works with all browsers.

Uhh, Brothercake’s script is almost as good and predates solutions shown here by about a year(?). http://www.brothercake.com/site/resources/scripts/domready/

  • Comment by: Damon Haidary
  • Posted:

@Damon I cited them twice in this thread…it was my preferred solution, I don’t want to steal anything to anybody, kudos to them, but as you may have read all these solutions, including my last one, are still too aproximative and somebody (me) need also the DOMContentLoaded functionality in other browsers missing it…

@Diego and @Damon,

I tried the Brothercake domFunction script but rejected it because often the onload event would fire before domFunction fired any scripts. This is the same reason I didn’t use Cameron Adams’ Scheduler onload script. I want events fire before onload or at worst be the first onload event and I want to use addevent to run DOMContentLoaded events.

You can see the results of my test at BrowserCam DOMContentLoaded 1 Project. Look at the difference in times between when the DOMContentLoaded events fire and the onload events. Look at how many browsers in which that is true. I do use a timer event for IE and browsers that don’t natively support the DOMContentLoaded event. I just back it up with an onload event to be sure that my events fire first.

Please look at what I’ve put together at DOMContentLoaded for Browsers, Part II. If this doesn’t work, please explain to me why so I can fix any problems. I’m open for comments.

Thank you,

Accessible javascript, part 1

@Tanny it was with your code that I made the test I talked about, dont’ use ‘DOMContentLoaded’ if your objective is start as soon as you have the BODY ready. ‘DOMContentLoaded’ is needed to wait for all the elements beeing in the BODY.

@Dean, you are correct about the 1px image at the end of the document, in fact in the comments in my onDOMLoaded.js I have:

 * Again, it may be redundant to say this but sounds
 * to me like we could avoid all this code by inserting:
 *
 * <img src="" onerror="fireEvents(this)" width="1" height="1"/>
 *
 * as the very last line before the BODY closes.

The situations we may face in real world applications are:

  1. I need to start appending DOM objects or writing to the document as soon as I can (this is normally when the BODY exsits and you can do appendChild to it).
  2. I need to add/change class or append behavior to all the elements of some type in the page as soon as I can (must be when all elements have been loaded).

A solution is needed for both cases, I will call the second one “onDOMLoaded” while the first should really be named something else having to do with the BODY to avoid confusion (onBODYLoaded is inline with the rest)

Well probably it was just me having confused things, sorry for consuming your bandwidth…grin!!!)

@Diego,

What I’m hearing is that we’re talking about two different things. I’m talking about when the whole DOM is loaded and you’re talking about when a specific element in the DOM has loaded. PPK apparently had problems running scripts before the end body tag with Safari.

At work we use Omniture for web site tracking. It loads a small image at the end of the page while sending information via the image src query string for tracking purposes. Because of our proxy server and other reasons, it sometimes take a very long time before the two by two pixel image was loaded. Menus didn’t work. The tree navigation didn’t work. A DOMContentLoaded event solves that problem for us.

Thank you,

@Tanny,

>>I’m talking about when the whole DOM is loaded and (1)
>>you’re talking about when a specific element in the DOM has loaded. (2)

doesn’t the first sentence also say the second one is true ?

and in the second sentence, if the specific element is the LAST element in the BODY
doesn’t that mean that the “whole DOM is loaded” ?

If everybody’s test cases are SHORTER than a TCP packet we will hardly fall in real world situations…make them at least 8 to 10 kbytes (just the HTML), only this way you will have reliable test, and shorter files will continue working.

I have a couple of questions. First, with all the back and forth, I just want to know which is the settled-on snippet? Diego’s last entry?

Also, forgive my ignorance and tangential query, but how does the if(/…) syntax work?

@Chuck, for most situation what we have (Dean/Matthias/John solution) is good enough, this solves how to fire an event as soon as the BODY is present to be able to attach your menus (for example). The rest are still open talks in my opinion…

If you have problems, like changing class or attaching events to a bunch of elements in the loaded page, then that solution is not the most adequate. My solution for this is not production ready yet, but open to tests/comments.

The syntax /xyz/ is a shorthand for new RegExp(“xyz”)…

@Chuck – Use the version I posted.

  • Comment by: -dean
  • Posted:

Thanks guys, for both the solution and the tutelage.

Can anyone verify if Herman’s post is correct about adding Konqueror support? If a trivial thing like changing:

if (/WebKit/i.test(navigator.userAgent))

to

if (/KHTML/i.test(navigator.userAgent))

works and does not remove support for all Webkit-enabled support (I believe it won’t remove Safari, but I don’t know if other Mac products that use WebKit keep KHTML in their user-agent string), then I suggest making such a change.

I would like this verified too.

  • Comment by: -dean
  • Posted:

I’ll check Konqueror tonight. If you have any particular tests you want me to run, I’ll be happy to give them a go.

  • Comment by: Daniel
  • Posted:

Tried it out, and it worked, see the result at http://daniel_james.fmail.co.uk/Screenshot.png. But it gives this error in the terminal:

konqueror: WARNING: getJSEventListener: event listener already found but with html=false - please report this, we thought it would never happen

No idea if that matters – most people won’t notice this as they probably won’t have launched konqueror from the terminal. And Linux programs often spit out errors along these lines.

Brian, if you’re worried you can just write: if (/KHTML|WebKit/i.test(navigator.userAgent))

  • Comment by: Daniel
  • Posted:

It seems that you could possibly write the defer script tag w/o @src tag for IE, and still pulls up just after a true ondocumentready would fire for a HTMLComponent?

In that case you can leave off the @src, and could help the https:// user above?

Any, others can confirm this on IE?? perhaps with a very complex DOM structure to load??

Sample page w/ additional HTC attached to test it’s ondocumentready event.

  • Comment by: Lucky
  • Posted:

User-Agent strings for several Mac browsers:

TrailBlazer (WebKit-based):
Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418 (KHTML, like Gecko)

Safari:
Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/418 (KHTML, like Gecko) Safari/417.9.3

Camino:
Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.0.3) Gecko/20060427 Camino/1.0.1

FireFox:
Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.0.4) Gecko/20060508 Firefox/1.5.0.4

It appears that KHTML is part of the WebKit string, but it’s unclear as to where the rest of the string comes from. The surprising part to me is the “PPC Mac” string under TrailBlazer, since this was run on an Intel Mac…

  • Comment by: rmoore
  • Posted:

Using window.onload over HTTPS Rob mentioned that the window.onload solution generates a secure/nonsecure warning when used over a secure connection. This is because the script’s src references the “javascript:” scheme instead of “https:”. I pointed the script’s src to “ht…

@Mario: Using “javascript:false” still produces the secure/nonsecure warning.

@Lucky: The defer attribute is rather inconsisted for inline scripts, so it can only really be used for external .js files. Check this article for more detail: http://www.websiteoptimization.com/speed/tweak/defer/

  • Comment by: Rob
  • Posted:

@Rob: great reading, i really appreciate it.

some extended testing can be done here and here.

  • Comment by: Lucky
  • Posted:

This Month In JavaScript – 2006.6

Guys, I’ve updated my Object Literal version to address the following:

Let me know what you think.

  • Comment by: Rob Cherny
  • Posted:

DOMContentLoaded for IE (and Safari) There is a problem with onload() event in IE – it fires only when everything on a page has been loaded (including images, Flash, or any other embedded objects). Dean Edwards has a solution that works similar to Mozilla’s DOMContentLoaded.  It w…

[…] size for users. Faster loading scripts — thanks to Dean Edward my scripts should now load when the DOM is loaded, but before all content (i.e., images) is done downloading. This shoul […]

This solution for https works, tested on IE7, IE6 and IE5.5 over 80 and 443, firing correctly onreadystatechange:

src=https:javascript:false

https:/// was a suggested solution, but caused errors on https pages (although OK on http).

My goof, that solution generates a request we don’t want. But this works:

src=//0

IE7b3 parses the triple-slash differently to previous versions. And we don’t need to include any protocol.

Test page here: https://www.partyark.co.uk/html/ie7b3success.asp

A

Alistair,

Does your solution work before images are loaded?

[…] terators for Prototype from Encytemedia A Prototype solution to the DOM Ready issue (see Dean’s post for background info) Javascript object < -> Rails object marshalling capability has b […]

Запускаем скрипт раньше события onload Аяксеры резонно замечают, что событие onload не всегда хорошо подходит для начала работы скрипта. К примеру,…

Just a heads up to anyone working in a team, or even by yourself, and IE keeps choking on random page loads with this method:

Make sure you’re not doing any DOM modification inline to the HTML.

Namely, I had some folks inserting Flash content with SWFObject, and the script tags were in the HTML, right after the Flash Containing Div.

This caused IE to randomly stop executing after hitting this script tag, and caused a headache of mythical proportions.

So the fix? Keep behavior separate from the DOM level, even against the wishes of those who prefer “convenience”.

[…] In de drietrapsraket van onderhoudbare HTML, onderhoudbare CSS en onderhoudbare JavaScript zijn we met de eerste goed op weg, maar valt er bij de laatste twee nog veel te winnen. Regelmatig zie ik een unobtrusive JavaScriptmenuutje uitgroeien tot een codemonster van 200 regels, omdat je én Dean Edward’s window.onload wilt toepassen, rekening wilt houden met memory leakage en ook nog toevallig sIFR van stal gehaald hebt. Alles geheel en al tot brei verwerkt. […]

Are there any known problems with defer sometimes hanging? I’m using Bob Osola’s pngfix.js, which uses the (external) DEFER technique to handle transparent PNGs, but about 10% of the time, IE hangs at “Downloading picture…”. This doesn’t occur when the same script is attached to onload. Degenerate fail case is at http://www.jay.fm/jaytest/jaytest.htm for the moment.

  • Comment by: Jay Levitt
  • Posted:

@Jay,
I don’t think it is a problem with the DEFER technique itself, I believe instead that when the script is executed, sometimes (as you stated), the picture is currently loading and the swap-out of the image will block this process leaving it in an unexpected state.

The fact that attaching this to the “onload” works correctly (also late), demonstrates this; at “onload” time the image is finished loading and you can safely remove/replace it. You cannot do that while the image is loading. It must be done before or after that.

[…] To solve this issue I used the solution proposed by Ross and ispired by Dean Edwards, Matthias Miller and John Resig that extends the Prototype library (my JavaScript framework of choice) to support a pseudo onload event for the DOM. […]

[…] window.onload(again) from Dean Edwards fixes the Internet Explorer needing an external JavaScript file thanks to Matthias. […]

I use another approach based upon testing for the body element, e.g.

setTimeout(warmStart, 50);

function warmStart()
{
  if (document.body)
     doSomethingUseful();
  else
    setTimeout(warmStart, 50);
}

of course this could easily be rewritten using setInterval, e.g.

var warmStartTimer = setInterval(warmstart, 50);

function warmStart()
{
  if (document.body)
  {
    clearInterval(warmStartTimer);
    doSomethingUseful():
  }
}

Eitherway, the technique allows you to start doing things before the onload event gets raised. Better yet it isn’t browser dependent!

  • Comment by: Dave Raggett
  • Posted:

@Dave – just because the body element is available doesn’t mean that the rest of the content is. This technique has been discussed many times in the past. It is unreliable.

  • Comment by: -dean
  • Posted:

@Dean,
you may be interested in the document.activeElement which is an IE only property that in my tests may replace the IE part of your script. Afther many test I will soon find some time to publish this at my test site.

The relevant docs for the activeElement Property and for the onactivate Event.

DOMActivate is infact in the DOM2 specs, and there is a bugzilla request for Support document.activeElement and document.hasFocus (IE’s focus model)

I couldn’t believe something existed from MS…I am very happy if it works for you…It works for me in the IPORT and I tested with Ben Nolan test case and the IE Back Button test case that you and Matthias pointed out.

I update my “DOMComplete” event test on FILE END comparison which works very well now on static HTML files. However that will be phased out soon in favor of new things I have come up (back to the old HEAD to discover aprox. file size).

The other good thing I found, also for IE, is the document.fileSize property which I will use in the “DOMComplete”, since I would also have some way of supporting older browser which just have the “onload” event available (for example every Opera 8 and Firefox older than 1.5). Please read the notes I have on DOMComplete.

Try this in IE for an example on document.activeElement (it will also work in other browsers older or newer), it can be a start for new code using it….

var d = document;
var progress = {
    loop: 0, prev: 0, size: 0,
    time: function (s) { window.status += ' * ' + s; },
    root: ((d.all && d.all.tags('HTML')) || d.getElementsByTagName('HTML'))[0],
    exec: function (f) {
        var t = this; t.size = t.root.innerHTML.length;
        if (d.activeElement) { setTimeout(f, 0); }
        if (t.size > t.prev) { t.time(t.size) ; t.prev = t.size; t.loop = 0; } else { t.loop++ }
        if (10 > t.loop) { setTimeout(function () { t.exec(f); }, 10); } else { setTimeout(f, 0); }
    }
}
progress.exec(init);

And don’t flame me about the names ok…:-) This is a cut and paste from a hundred like it. Just hoping and trying to avoid confusion with other…

Maybe this is too simple, but it’s what I use and has never failed:

  function init() {
      if (document.body) {
          /* function body goes here */
      } else setTimeout(init, 99);
  }
  init();
  • Comment by: brandon
  • Posted:

I have uploaded a test case of DOMComplete which adds the use of the “onactivate” event attached to the document for all versions of Internet Explorer. Unfortunately I only have IE6/7 to try.

Please report your problems/success or send me the test cases that does not work. I am very interested in to see if this can be a valid replacement method for the document.write trick we currently have for IE.

If one of the “onactivate” or the “onbeforeactivate” event works well, we don’t need to poll for the document.activeElement, an event is less resource consuming and a cleaner method if available.

After more test cases has come in and after Matthias Miller pointed out that using “activate” may preclude the use of the “focus” event it shows that these methods are still not reliable and they still fire to early.

I had to reconsider instead the use of the “document.fileSize” which for IE remains the only property that gives solid and “harder to fool” page load events.

The “document.fileSize” property for IE seems to be highly independant from the test cases I got to run, we don’t have an event for IE yet but polling is not a bad thing.

The “document.fileSize” property seems to changes from an initial “unknown” string to the complete file length in one shot, I tested it by introducing a forced 5 seconds “delay” in the ouput of the same page renamed with a “.php” filename extension.

With that example it is clear that the behavior of DOMContentLoaded for Firefox/Opera is fully replicated, all browsers will now wait for that “delay” time before firing the event.

Does this mean we can safely use the “document.fileSize” property in IE ?

Here are the test case and the updated DOMComplete I have on my test site. Works for me on Mozilla / Firefox / IE / Opera and Konqueror and may be used in environments where several windows/documents are needed.

@Dave,
your suggested method (#96) of polling for the BODY until it is different from null is OK for me in Internet Explorer, I still have not seen a test case breaking that method in IE.

From my tests, and for a script residing in the HEAD section of the document there are only two “moments” in IE, the first beeing the HEAD is parsed and available, the second beeing the BODY is parsed and available to scripts.

One can get to smaller chunks of the BODY only with INLINE scripts in the HTML while it is loading, but from my understanding that method of mixing things is banned.

We have probably fall in to think that the existance of the BODY doesn’t mean that everything is loaded. This is correct, but apparently in IE there are no intermediate points where you will find just half of the nodes. Once the BODY is there all of it’s subnodes are also in the DOM.

Still there are reports claiming that the method is not reliable. I would like to find a test case showing I am wrong. Dean if you happen to have one at hand, please post it…

javascript:void(0) causes a security alert in IE when called from https

only solution ive found so far is pointing it at an empty file.

  • Comment by: Rene Koch
  • Posted:

[…] The startup time of Dojo has improved, and it it due to DOMContentLoaded now being the onload event. This last weekend I finally implemented Dean Edwards’ DOMContentLoaded (with Matthias Miller and John Resig) solution for the dojo.addOnLoad event, as opposed to using the window.onload event to fire all initialization…and so far (with the exception of some minor side effects, notably with widgets that use images for layout) it is working very well–and the bottleneck of initial load performance (you know, where the page seems to take forever to load) has been markedly improved. […]

[…] http://dean.edwards.name/weblog/2006/06/again/ […]

Hello Dean:)

what’s wrong with this way ? (something like #100)

(function(){if(document.body)init();else setTimeout(arguments.callee,0);})();

I’ve successfully tested with IE5, 5.5, 6, 7b3, Opera 8, 9, FireFox (I don’t know about Safari or KDE)

example page

We have probably fall in to think that the existance of the BODY doesn’t mean that everything is loaded.

Do we need all nodes or something else ?

an inline external script place is exactly inside the head tag, every other script tag will be part of the body.

If a script is wrote after the body then every method is not usefull because you just have the body (same as document.body) and every document node is available … then I wonder why document.body, platform indipendent, inline and without a global funcion name, in my way, shouldn’t be the correct way to have an onload event exactly when the body, then the layout, is totally available. Head external scripts ? They’re just available before the document.body too. Then what’s wrong with this way ?

Just another test shows that anonymous function delay is everytime the same or bigger (few milliseconds) than defer method or DOMContentLoaded event. I think then that anonymous function with document.body check is the best way to solve onload problem for portability and compatibility (and it’s a single line few chars trick, then it’s simple to change too). What do you think about it, Dean ? ( please, don’t tell me that a maximum of 30/1000 second is a problem for your code execution:D)

Finally, this should be a way to solve every onload page problem just adding this piece of code.

(function(){if(document.body){onload();delete onload;}else{setTimeout(arguments.callee,0);}})();

example page

… ehr … sorry guys … I mean this piece of code …

(function(){if(document.body){onload();onload=function(){};}else{setTimeout(arguments.callee,1);}})();

@Andrea – this is not a reliable way to check that the DOM has completely loaded. It seems that every twenty comments or so someone will mention it. The only way to stop this is to turn off comments (again).

  • Comment by: -dean
  • Posted:

[…] window.onload (again) onload ondomcomplete (tags: javascript) […]

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

[…] I have also incorporated Dean Edwards’ superb onload replacement to get the ball rolling before images are loaded. It’s a neat trick and takes 5 minutes to refactor it in. […]

Javascript Updates I’ve updated the url_template.js and json_form.js libraries to fix some bugs, to make the demo I gave at XTech run more smoothly. It should work well on Safari, Mozilla…

While testing on Netscape 6.0 (Gecko/20001108), I was getting a "nested comment" error in the JavaScript console, which halted the execution of the otherwise nicely working script, not firing init() at all.

I’ve tested the IE CC can be simplified like this, to avoid having nested comments:

/* for Internet Explorer */
/*@cc_on 
@if (@_win32)
    [...]
@end @*/

Since we’re only targeting IE, we can keep it all inside a single comment, no need to be opening and closing it.

[…] Therefore, I have with great interest followed the work of Dean Edwards, together with Matthias Miller and John Resig, and the exciting conclusion they came to in window.onload (again) […]

Hi,My issue is different,I have few problems with IE 7 beta 2,With 1 Exception,I cannot view or run my Norton 2006 Antivirus,Or anti spam,or my system works,Pass word manager,All tese windows are blank.I contacted norton tech’s,because it was all working fine for 6 weeks.And then the blank white windows in the above norton products.And Norton told me that they do not support IE 7,This is when i contacted them,But i think this is not all IE 7 ‘s problem.Has any one else experienced similar problems with Norton 2006 and IE 7 beta 2,And if so,How do we fix this problem,As Norton told me to uninstall IE 7,But if i do this ,all the updates for IE 7 will not work,As they are not uninstaled with IE 7,And this would create security issues.As Norton is working,It updates and firewall is working,Anti virus is catching viruses, But i cannot view any of this,As all i get is a blank white window.but Norton insists that the problem is with IE 7.

  • Comment by: Nigel
  • Posted:

Scripting presentational markup

Regarding comments 52 and 53 above (IE over https) I’ve found that it is possible to combine the original way with Matthias Miller’s contribution by using “.” as the filename. I might have shown an example, but seem unable to get past the XHTML filter and frankly have better ways to spend my time.

Regards

  • Comment by: Kyle
  • Posted:

Hi,

I’ve been using this great solution for quite a long time now, and I have two questions :

– Why do you use @if (@_win32) ? I suppose it won’t work on 64 bits OS, and is

/*@cc_on doSomething();@*/

not enough (as far as I know, IE is the only browser to support these conditional comments) ?

– What do you think about this solution :

HTML :

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

Javascript :

/*@cc_on init(); // call the onload handler @*/
  • Comment by: Eldebaran
  • Posted:

IE7 and minWidth

[…] http://dean.edwards.name/weblog/2006/06/again/ […]

A couple of thoughts:

(a) What happens if window.onload, or DOMContentLoaded, is fired before the script gets a chance to attach event handlers – or is this very unlikely.

(b) This is an untested idea, but could you set an interval going to look at the lastChild of document.body. The idea being that if this exists and does not change for say 10 iterations, you take the view that it is safe to assume that content has loaded?

  • Comment by: Julian Turner
  • Posted:

[…] http://dean.edwards.name/weblog/2006/06/again/?full#comments […]

@Julian, point (a) is very unlikely to happend in real world, but I believe it is possible in situations where there are no images and the page is shorter than the javascript it includes (ex: an emtpy body and a big javascript). Most implementations of DOMContentLoaded just respond to the first event that they can get (in this special case the ONLOAD) and then cancel the others methods so they will never fire. The init function is still called by the onload, that’s not the problem, the problem might be that the called script is not there yet.

Point (b) is something I have tried with some success in HTML pages, the troubles arise when one have to handle generated dynamic pages with possible delays in between line of output. In that case the HTTP Response will be “chunked” and this kind of checks will fail. Since you do not know how long the delays will be.

We’ve applied the iframe src=”javascript:false” … idea to stop the warning message in IE, which works fine. However we now annoyingly get the word ‘false’ rendering on the frame when we first display it using http. It only lasts a second or so and then the proper content is rendered, but we can’t send it out to customers with random words appearing on popup screens. Any ideas that will work okay with both https and http?

Thanks Steve

  • Comment by: Steve
  • Posted:

You suck as a programmer – totally.

  • Comment by: it dont matter
  • Posted:

The example included here is a BAD, very BAD BUG that I believe have something to do with our event handlers or at least with some false positives. This example does not work in Internet Explorer and the results are not consistent with all the other browsers.

None of these events are ever fired on Internet Explorer, so I thought somebody may be interested. This bug was hitting me silently for who know how long.

<html>
<head>
<title>TEST FAILS on top, self and parent properties of window object</title>
<script type="text/javascript">
    alert(top + ' ' + self + ' ' + parent + ' ' + window);
    alert((top == window) + ' ' + (self == window) + ' ' + (parent == window) + ' ' + (this == window));
    alert((top === window) + ' ' + (self === window) + ' ' + (parent === window) + ' ' + (this === window));

    try {
        if (parent != null) { parent.onload = function (e) { alert('PARENT fired the event !'); }; }
        if (self != null) { self.onload = function (e) { alert('SELF fired the event !'); }; }
        if (top != null) { top.onload = function (e) { alert('TOP fired the event !'); }; }
    } catch (e) {
        alert(e);
    }
</script>
</head>
<body>
</body>
</html>

Do not trust on “self” being the same object as “window” or an alias of it, do not trust either on believing that “top” or “parent” may point to the same “window” if there is only one window (that was my wrong assumptions).

If you need to check them be sure to use the “===” (3 equals) strict notations.

I still does not understand very well the relationships between these keywords in Explorer, they surely are aliases of the window object in all other browsers.

This was one of the hardest bug to find ever….:-(

Hope you appreciate the informations and it would be great if someone can enlighten me or show me where I am wrong…

“There is no public standard that applies to window object” – that’s all.

Therefore there is no need to hope, that functionality of attributes and methods of Window Object will be identical in all browsers. In IE Window methods (window.open, for example) have no call method. But should it be considered as a bug? Who has told, that this method should be implemented? And coming back to parent.onload, what necessity for it? We have window.onload…

In general, IE is not a simple browser. In a code of the program you receive window === this, and in intrinsic events window !== this. All because in intrinsic events window is only attribute of Window Object (i.e. window.window || window.self).

Try this in body tag:

onload=’alert([this === window, this.self === window])’

  • Comment by: AKS
  • Posted:

So for those of us wanting the latest, greatest version of this, is there an up to date example or do we need to piece it together from the 132 comments above?

  • Comment by: Steve
  • Posted:

@Steve – I’ll post an update soon. The final solution is not very different to this one. If you are using HTTPS then change this line:

document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");

to this:

document.write("<script id=__ie_onload defer src=//:><\/script>");
  • Comment by: -dean
  • Posted:

I tried incorporating this adapted solution from Matthias Miller into a project I’m working on, but I discovered some strange behavior on pages without images. I expect that any substitute for DOMContentLoaded will always end up being called before window.onload. This, however, is not the case with this onreadystatechange solution, although it is the case with the previous two solutions which rely on an external JS file loaded via script[defer] and an external HTC file, respectively.

I have put together a test case which demonstrates the issue I discovered (view in MSIE): http://weston.ruter.net/projects/pre-onload/

Now I now not why nobody thought of this before: the https solution that works with the solution above AND with adding the script through the DOM is dead easy: src=””.

Problem solved :). Tested on my own server and never got that alert again.

  • Comment by: Stefan Van Reeth
  • Posted:

[…] Dean Edwards’ fantastic work with checking for DOM loading and proper adding of events. […]

In the code:

/*@if (@_win32)
... code ...
/*@end @*/

what are the ‘@’ characters for?

This is the first time I see this, and since I’m still learning JavaScript, I don’t know what they mean.

  • Comment by: Jon
  • Posted:

@Weston,
thank you for your excellent explanation and effective testcase to demonstrate this IE misbehavior, I have been trying to spot this problem in previous messages but without success. Thank you for providing that testcase.

So, to fire when ALL NODES have been parsed and inserted in the DOM, we have to go back to the previous solutions that Dean suggested, with the conditional script.

However after many tests I am now using this:

<!--[if IE]><script defer>var IELoaded = true; init();</script><![endif]-->

There is no external src file, to avoid the protocol switching problem (HTTP/HTTPS), there is no need to setup an event handler nor doing a document.write, the call to the init() function is still deferred, one could also setup a timer and wait for the IELoaded variable to have a “true” value.

I hope this can fit still more cases where previous solutions did not work.

For those who still want to attach an event handler just add and ID as usual to the conditional script, there is no need to do a document.write, I will try to avoid that if possible.

[…] Another point it can be improved on—which is something to keep in mind for all unobtrusive libraries—is performance. By using the onload event, your DOM related behavior is only applied after a whole page, including all of its assets, is loaded. A better alternative would be the DOMContentLoaded event, which lets you apply your behavior as soon as a page’s DOM is available. Because the DOMContentLoaded event is not fully supported across browsers yet, you could use this solution instead. […]

Sorry for being WRONG on #139 above,
that conditional line will do only if it is the last line after the BODY close tag, and just before the HTML closed tag, much like with the IMG trick, but this is NOT what everybody wants.

It will just “defer” execution in the HEAD context if you put it in the HEAD section. So not very useful to wait for all the nodes being present in the body.

[…] A big issue that I have with the implementation is that it promotes the use of the window onload event as a means of knowing when the DOM is loaded and ready. A better solution has been available since June 2006. There is no reason to use window.onload to wait to do DOM traversal. (There are good reasons to wait for window.onload – but they mostly have to do with images loading.) […]

I just tried to find a solution for the same problem and ended up with a script that might prove quite interesting. It may also turn out to be crap, as I haven’t tested it thoroughly, yet, but I share it with you anyway:

window.onload = function() {
   // Finally call ondomready if it has not yet been called
   if(window.ondomready && !window.ondomready.called) {
      window.ondomready();
      window.ondomready.called = true;
   }
}
window.ondomready = function() {
   // ...
}
// Firefox & Opera
if(document.addEventListener) {
   document.addEventListener("DOMContentLoaded",
     function() {
       if(window.ondomready) window.ondomready();
       window.ondomready.called = true;
     },  false);
// IE
} else {
    var scr = document.createElement("script");
    document.getElementsByTagName("head")[0].appendChild(scr);
    scr.onreadystatechange = inject;
}
// Inject script code into the document
function inject() {
   try {
      var script = document.createElement("script");
      script.text = "if(window.ondomready && !window.ondomready.called) { " +
        "window.ondomready();" +
        "window.ondomready.called = true; }";
       document.appendChild(script);
   } catch(e) {}
}
// Safari, ...	
try {
   if(!document.all) inject();
} catch(e) {}

Both IE and Safari execute scripts in a script tag, even if they are actually outside of the document’s root node. This means if we were to insert a dynamically generated script after the HTML element, it would get executed right after the whole DOM is available and before the window.onload event. Unfortunately IE will give you an error and even stop loading the page if you try to insert the script right away. This is not the case when you link the insertion if the script to the onreadystatechange event of another script tag.

The above script works in Firefox 2, Opera 9, IE7 and Swift 0.2 (based on WebKit) on WinXP.

  • Comment by: AKonrad
  • Posted:

[…] Another problem with window.onload is that it has been interpreted differently by different browsers. When dealing with this issue, I came across this article containing a multiple browser solution for window.onload. As yet I haven’t tested it in anything but Firefox and IE6, but it works well in both of those. For future reference, here is the conglomerated script from Dean Edwards and friends: […]

Small question.

What happens when you have an iframe that contains a large image that downloads slowly? The Dean defer script will fire AFTER the large image has downloaded, instead of before.

I don’t see any nice way around that problem.

  • Comment by: Eric Gerds
  • Posted:

Eric,
for newer browser (Mozilla Firefox and probably Opera and Safari) there is a “DOMFrameContentLoaded” event that will fire on the main document when the DOM of the IFRAME is completely loaded, just like with the “DOMContentLoaded” but for iframes. With other browsers use timers/interval to check for the BODY.

However the most important thing is that if in your code you can change all the “appendChild” instances to “insertBefore” there is NO NEED to fire when ALL elements are loaded and you can add content while the page is still loading even in IE6/7. In most circumstances I solved this way, also it seems there still is need for Dean trick or similar in some special case where you really need a collection of all the document nodes.

For some insight on the reason why “in some case” changing the “appendChild” call with an “insertBefore” call will work please read Sam Ruby article Thats-Not-Write and related comments, especially the pictures where he explains about the nodes.

These methods have strongly influenced the way I write my own widgets, and I firstly noticed that in another simple test case I once borrowed from Weston Ruter, another cute guy that in the past also gave his contribute to this cause.

Thanks Diego. I didn’t know about the “DOMFrameContentLoaded” event. My question arises due to IE behavior, however, not Firefox/Mozilla.

When you have one or more iframes (containing large images), then IE will cause the script.readyState to be “complete” AFTER the iframe content has fully downloaded. So, it would appear that the Dean script works great as long as there are no iframes. With frames, a different technique would perhaps be needed.

I am not particularly fond of timers. I think in that case I could use a simple init() command before the end of the body tag, which would also guarantee that “DOMLoaded” fires before Window Loaded. That compromises the principle of separation, I know.

The thing about timers is:

1) There is the possibility that window.onload may fire BEFORE your timer detects that the dom has loaded. In most cases, that is no big deal.

But in some circumstances, you may need to have your DOMLoaded events to occur before Window Loaded (at least for most browsers). Theoretically, the DOM Loaded event should never fire after the Window loaded event.

For short web pages that load very quickly, the script.onreadystatechange indeed does fire a little after window.onload fires. Interestingly, document.readyState == "complete" occurs anywhere from less than 1 ms to 16ms sooner than window.onload.

2) Trying to set some some small interval to perform some function seems like a hit on CPU. Especially when other scripts might also be using timers for similar reasons.

BTW, I can understand the need to try to achieve some method of strictly achieving separation of behavior from the HTML. But it is the lack of browser support for this principle that is the real cause of the problem. In the meantime, I think that small compromises in separation ( like init() at the end of the body tag) can make sense.

  • Comment by: Eric Gerds
  • Posted:

Eric,
I can confirm what you say here on Internet Explorer:

For short web pages that load very quickly, the script.onreadystatechange indeed does fire a little after window.onload fires. Interestingly, document.readyState == “complete” occurs anywhere from less than 1 ms to 16ms sooner than window.onload.

In IE the “readystatechange” event seems to fire first on the document and then on the script element.

YES, timers may produce extra slowness / sloppiness in the UI, so use events depending on your context/needs:

1) if your primary objective is just precede the standard “onload” event just use:

addEvent(document,'readystatechange',fn);

and check for the DOCUMENT “readyState” to be “complete”. The drawback is that by using this event also images will be loaded at that time (which I believe is what most people are trying to avoid).

2) if you instead want to fire as soon as the DOM is ready to accept modifications/additions use the same event but instead wait for the DOCUMENT “readyState” to be “interactive” and use insertBefore instead of appendChild. At first is not really easy because you have to pass an extra reference node to the insertBefore call but when you grasp the trick it’s as easy as with appendChild, and as a bonus your scripts may become more accessible and usable and may also decrease your load time.

In short I would say that:

“scripts should take care not to insert DOM elements in the wrong place while the page is loading”.

I know this sentence is not very helpful but bear with it and have a look at Sam article mentioned above.

It would be nice to have brighter comments on this from more knowledgeable peoples than me though.
Hope some of this may be of help for you and others.

@Diego, I’ve played with readyState a lot and have never been able to find any consistent behaviour for this property.

  • Comment by: -dean
  • Posted:

Dean,
I know you have made so many tests and repeated this so many time here, so sorry for bringing that back again.

I believe that the DOCUMENT “readyState” property is unreliable JUST if we try to use it to determine when ALL the nodes have been parsed/loaded in the DOM (images and other bins excluded). In this case it is not reliable 100%, you are correct, but if one doesn’t mind firing after the images are loaded this is the only way I found to fire BEFORE the “onload”event, sometime just a millisecond before, but always before it, even with pages with no images or other bins in it.

However I found it stable enough in IE if I wait for the “interactive” state instead of “complete” state, then I just have to make sure to use insertBefore instead of appendChild to avoid problems with IE. In other browsers it doesn’t matter it will work both ways.

When the DOCUMENT “readyState” is “interactive” it looks like I am able to insertBefore nodes safely in IE if I avoid the current parser insertion point. At this point the BODY is available and other things have already been setup by the browser; queries to the current DOM can be done and other script inserted in the HEAD section.

Using this technique It is possible to add element to the DOM while the page is loading, contrary to what we have learned in the past about IE, we just have to avoid manipulating around the current parser insertion point by using the following syntax or similar.

document.body.insertBefore( element-to-add , document.body.firstChild );

I understand there are some limitations with this solution (the sequence of the node s is different in the document flow), but I believe the CSS positioning properties will make this simple to overcome. This is how I started to test and use this method.

Appreciate your comments.

/*@cc_on @*/
/*@if (@_win32)

if (document.attachEvent){
document.attachEvent(‘onreadystatechange’,
function(){ if ( document.readyState == "complete" ) Init() } )
};

document.write("<scr" +"ipt id=__ie_onload defer src=’//0′><\/scr" + "ipt>");
script = document.getElementById("__ie_onload");

script.onreadystatechange = function(){if (this.readyState == "complete") Init()};

/*@end @*/

For short web pages , the document.readyState insures that DOMLoaded fires before window.onload. For large pages with images, the script.readyState will fire before the images download.

  • Comment by: Eric Gerds
  • Posted:

Eric,
combining different events also requires some check to be done so that init() is called only once, and better remove the registered events once they have finish their job.

Combining several events as you have done, may better match the original scope of this thread, since it was about an emulation of “DOMContentLoaded” for IE, and it seems that everybody wants a collections of ALL the nodes in the document, even if they don’t need it…;-)

However this is what I would use to start attaching widgets/elements to the BODY by using insertBefore and that is the fastest way to start attaching nodes in IE.

if (document.attachEvent) {
   document.attachEvent(’onreadystatechange’,
      function(e){
         if (document.readyState == "interactive") {
            document.detachEvent(e.type,arguments.callee);
            Init();
         }
      }
   );
}

As you can see I went back to BrotherCake solution and just wait for the BODY, the timers/intervals have been substituted by event handlers so it is faster and less demanding in CPU and hopefully we have got a little more knowledge about our beloved events.

Cheers

[…] Dean Edwards has a solution that works similar to Mozilla’s DOMContentLoaded. […]

[…] Supports DOMContentLoaded […]

Hello,

I found your site because I was looking for a solution to replace my onload event. If you look at this site:

http://www.rodnelman.com/gallery.html

The transparency loads after everything else (thumbnails, text). I am JavaScript-ignorant and would like to know if I can apply your script to my page? Thanks

  • Comment by: Eduardo
  • Posted:

Please dont cry… anybody… just tell me if this does the job? Read Andrea Giammarchi’s, Dean inspired contribution, but found it difficult to decipher and a lot of flaming seemed to abound on his blog. I dont care who did what first what way, just to find something that works. And back into the shadows go I to lurk….

onDocument = function ( lastFunc ) {	
		// onDocument.ready allows multiple subscriptions to the onDocument 'event handler' by using nested closures. 		
		onDocument[" ready "] = (function (priorFunc){
			return function () {
				onDocument[" ready "] = function (){};
				if(!arguments.callee.executed) {
					arguments.callee.executed = true; 
					if (priorFunc) priorFunc(); 
					lastFunc();
				}
			}
		})(onDocument[" ready "]);

		/*@cc_on @if(!@_jscript)*/	

		// support for FireFox and Opera 9+
		if(document.addEventListener) document.addEventListener(" DOMContentLoaded ", onDocument[" ready "], false);

		// support for Safari, KDE and Opera 8.5
		if ( /WebKit|Khtml/i.test(navigator.userAgent) || (window.opera && parseInt(window.opera.version())<9)) (function(){
			/loaded|complete/.test(document.readyState) ? onDocument["ready"]() : setTimeout(arguments.callee, 1);
		})(); 	

		/* // support for MSIE
		@else 
			var jsOnStatus = "(function(eScript){if(eScript.readyState==='complete'){eScript.parentNode.removeChild(eScript);onDocument.ready();}})(this)";
			document.write("<script defer src='//:' onreadystatechange=\""+jsOnStatus+"\"></"+"script>");
		@end @*/
	};
  • Comment by: davi
  • Posted:

… apart from coming back briefly to clarify, the above is just a rewrite of other peoples work; Dean, you rock, delete my posts if they add confusion…

  • Comment by: davi
  • Posted:

[…] First Check out Dean Edwards Article on window.onload.   Dean is a smart guy, and really kicked out something nice with his window.onload fix, or rather a pre-emptive check.   While Dean may have inspired this idea, it was the lack of support for a few things that I was really after.   Thanks to a buddy of mine Robert Evans and a chat over lunch about how great it would be to be able to include the javascript libraries you wanted within a javascript file and not in the HTML the include.js library was born.   Then as I was building this thing out, I realized that I could actually load my javascript files faster if I used asynchronous XMLHTTPRequests (98% of browsers support this) and extend it just a little further to have load functions as soon as its done and actually get all of the goodness that Dean’s solution offers and more. […]

[…] En Juin 2006, avec l’aide de Matthias Miller, il a complété et amélioré pour IE (plus de dépendance par rapport un fichier externe (via ). […]

This is great, but is there a non-IE equivalent of the following code: ?

if ( document.readyState == “complete” )

I have some cases where I try using the onload handler (IE) or the DOMContentLoaded event, only to discover (by the lack of anything happening) that the events must have already been thrown.

The situation is that I have an iframe which wants to dynamically add content into the parent page. For IE I can poll parent.document.readyState, but for non-IE there doesn’t seem to be a property. I had high hopes once I found Dean’s work, but it seems to not meet my requirements.

The iframe code has no idea whether or not the parent page has finished loading, so just signing up for an onload/DOMContentLoaded event is no good.

Any ideas ?

  • Comment by: dd
  • Posted:

I was having the same issue with IE7/IE6 not firing the functions or event attachs because im using ASP to load sets of includes to build the page.

So every so often the events wouldnt happen because i assume the objects it was looking for were not present. I test for them but with no redundancy.

ie6/7 init() code below

var tab = document.getElementById('buttons'); if(tab){ // do stuff }

Since this failed sometimes in IE, you need some redundancy, this works in all browsers regardless, and removes the body.onload need.

if (!tab) { setTimeout(init, 99); }

so if it can’t find it, it will check back. This has not failed once since implementing it. And it’s scalable.

if ((!tab) || (!anotherObj) || (!anotherObj)) { setTimeout(init, 99); }

I also noted any inline script calls, cause it to fail in the same intermitent/occasional manner, since some pages use flash i tried using the “startIeFix()” “endIeFix()” to remove the need to click to activate flash object in IE. Once this was removed the pages with flash also stopped doing it.

Soo superfly! Solves my problem perfectly!

  • Comment by: Bjordan
  • Posted:

Would this be revisited (again) soon?:)I’m getting quite confused by all the comments.

@Micheal – OK.:-)

  • Comment by: -dean
  • Posted:

I second the request for revisitation– there’s no better equivalent of this thread anywhere and it would be quite a contribution to put together all the subsequent comments into a new entry… thanks Dean!

  • Comment by: Kalvin
  • Posted:

Dean, will this be an option, or have I missed the reason why we shouldn’t use it ?

<!--[if IE]>
<script type="text/javascript" src="//:" defer="defer" onreadystatechange="if(this.readyState == 'complete') { init(); }"></script>
<![endif]-->

Diego

@Diego – that looks fine. The posted solution is the same, except that your readystatechange handler is inline. It is more readable though.:-)

  • Comment by: -dean
  • Posted:

I am a designer, not a programmer. As such, I’m willing to pay $50 via PayPal for assistance in implementing Dean’s code on one of my existing websites. I contacted Dean directly but he doesn’t have time available right now, and the local programmers I work with haven’t been able to help me out either. If you are intersted and have time available, please email me at kevinl@directlogistics.com. Thank you.

  • Comment by: Kevin
  • Posted:

Thanks, Dean! This is exactly what I’ve been looking for for the menus on http://carolinalumber.com. Works like a charm!

  • Comment by: Jason
  • Posted:

There any updates to this working with IE? I can’t get it to work, getting an ‘Element’ undefined error.

Maybe some other solutions posted in full other than piece by piece?

  • Comment by: RRasco
  • Posted:

[…] However, there is a problem we run into when loading the page over a slow connection: the Javascript is not run until window.onload, meaning our users suffer as they can see part of the page rendered but not use it until it’s fully loaded. For those who don’t know, window.onload will not execute until all images, CSS, and Javascript files have loaded. Our first solution to this problem was to use onDOMReady. This has worked fairly well and kept our site running reasonably quickly. We’ve run into problems with onDOMReady in IE6 however, and as a result disabled it in favor of having our pages render all the time. In the IE family of browsers if a script tries to access a part of the DOM before it has been completely processed the browser will raise an exception “operation aborted”. After this alert message pops up, the web page becomes unusable and the browser could even crash. […]

Dean,
in the online Microsoft documentation About Element Behaviors I have found:

A few methods, such as doScroll, require the primary document to be completely loaded. If these methods are part of an initialization function, they should be handled when the ondocumentready event fires.

The doScroll() method return “Unspecified Error” if called before the page is completely loaded.

So why not test that method until it does not return an error like this:


var timer = setInterval(
	function () {
		try {
			document.documentElement.doScroll('left');
			timer = clearInterval(timer);
			init();
		} catch (e) {
		}
	}, 50
);

A slightly more elaborate script and related tests can be found here as usual.

Diego

[…] Using window.onload over HTTPS Filed under: Internet Explorer — outofhanwell @ 2:36 pm Rob mentioned that the window.onload solution generates a secure/nonsecure warning when used over a secure connection. This is because the script’s src references the “javascript:” scheme instead of “https:”. […]

[…] We have now had, for a while, a way of emulating the “DOMContentLoaded” event across all the main browsers. For those of you who don’t know what I’m talking about, a web browser fires an event, “load”, when a page finishes loading, and JavaScript can attach code to that event which will then run once the page has loaded. However, if there are big images or other slow-loading things in the page then the code gets delayed until after they’ve finished loading too; this is annoying, because most DOM scripting doesn’t actually need the images to be loaded, and so the delay is unnecessary. Opera and Mozilla fire a “DOMContentLoaded” event which runs after the page has loaded but before all the slow images and applets and Flash movies need to load, which is perfect. IE and Safari don’t fire such an event, but it’s been possible to do IE and Safari-specific things to make it happen. (In Safari you check document.readyState, in IE you document.write a <script> element with defer set. See Dean’s writeup.) If you’re using a JavaScript library of some sort (jQuery, Prototype, Mootools, whatever) then all this complexity gets wrapped up for you and you don’t have to worry about it, but if you’re writing stand-alone scripts (like my sorttable), which don’t depend on a library, then this is more of a problem; you have to include a big swathe of boilerplate code to emulate the DOMContentLoaded event. […]

[…] Hedger Wang has a different solution for the DOMContentLoaded issue that Dean Edwards and YUI also solves. […]

A small change to the above code snippet.

Use “doScroll(‘none’)” instead of “doScroll(‘left’)”, this will hopefully be less ambiguous to people and help understand that we are not trying to do any scroll in the page…

Forget the above messages.

The solution will only work with “doScroll(‘left’)”, “none” will defeat the trick.

[…] Dean Edwards: window.onload (again) That my friend, is the window.onload problem. The onload event waits for all binary content to download before firing. No kitty-tickilng until then. (tags: javascript) […]

John Resig solution as posted here does not work if the page is refreshed, but does work when entering the site or hitting enter in the adress bar.

  • Comment by: Anders Jenbo
  • 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 […]

Hi! I’m using the Cross Browser Solution from this side (#5)

While testing my site with Safari beta 3.0.4 for windows I noticed that the init() function is not executed.

Obviously Safari doesn’t like this code:

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

When this code is disabled, Safari executes the init() function again.

So I changed the order,used else ifs and put the webkit branch first.

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

I haven’t tested this completely yet, can anyone confirm the Safari/Win problem an test t?

  • Comment by: Kai
  • Posted:

[…] In addition, some browsers expose a method for determining when the DOM is done being constructed, but images and such have not finished loading. Firefox calls this onDOMContentLoaded and there are hacks to get it supported on other browsers. This is really handy; when you’re pulling content from edge cached locations you don’t want to wait for all those locations to answer DNS and respond before you can get on with setting up your page and responding to user events. […]

[…] http://dean.edwards.name/weblog/2006/06/again/ […]

[…] Anyway, on page load (either window.onload or whatever enhanced version you use- I use this ) I run this block: […]

[…] 把Dean Edwards的解决方案封装一下, 方面以后调用. function onDomLoaded(func){ function init() { if (arguments.callee.done) return; arguments.callee.done = true; if (_timer) clearInterval(_timer); //do stuff func(); }; if (document.addEventListener) { document.addEventListener(”DOMContentLoaded”, init, false); } /*@cc_on @*/ /*@if (@_win32) document.write(”>script id=__ie_onload defer src=javascript:void(0)/script var script = document.getElementById(”__ie_onload”); script.onreadystatechange = function() { if (this.readyState == “complete”) { init(); // call the onload handler } }; /*@end @*/ if (/WebKit/i.test(navigator.userAgent)) { var _timer = setInterval(function() { if (/loaded|complete/.test(document.readyState)) { init(); } }, 10); } window.onload = init; } […]

@Eldebaran (comment #122) To get it work on Windows 64bit platform(s) you have to change slightly IE conditional statement: /* for Internet Explorer */ /*@cc_on @*/ /*@if (@_win32 || @_win64) …

[…] window.onload (again) […]

Has anybody tried this solution with multiple scripts with defer attribute? If there are multiple scripts with defer, it hangs IE7 and works inconsistently in IE6…Any ideas?

  • Comment by: Jitendra
  • Posted:

It seems that the addLoadEvent method can not be used more than once in IE7(Maybe IE7-),if multiple addLoadEvent call occurs,only the last registed handler through addLoadEvent will be triggered,and thus,the rest will be simply ignored.How can i get around this problem in MSIE?? Any idea please contact me:lenatis@gmail.com

  • Comment by: Lenatis
  • Posted:

[…] the best total solution has been crafted by the venerable Dean Edwards who pulled concepts from the solution engineered by Matthias Miller. Another possibility is this […]

[…] I ran into the IE6 window.onload issue and subsequently came across Dean Edwards et al’s solution via Google. This solution was posted in 2006, and since then YUI and other JavaScript frameworks […]

[…] a onload alternative [[g: javascript onload alternative /]] I stumbled upon quite an interesting article by Dean Edwards. He has a site that previously was unknown to me but seems like quite a […]

[…] file. The JavaScript file includes code to run the resizing function when the DOM has loaded, using Dean Edwards’ script, so the init function will look a little different to the one on this […]

I am working on an article that uses your collection of solutions for DOMReady, though it seems my version of Safari (3.1.2) executes the block of code intended for Mozilla/Opera (and thus also executes the /Webkit/.test() block. Am I special?

  • Comment by: Dave Artz
  • Posted:

[…] building a unified interface for this has been done by Dean Edwards, John Resig, and others – see this discussion on Dean’s site for some background. Concrete implementations are provided by jQuery, MooTools, and […]

[…] the readyState property on a document but has no event for watching it, therefore you could set a timeout to watch for it to […]

Hi, Dean and all!

This is a fully validated and verified version, it does not need conditional hacks, iframe hacks, https hacks! It has NO memory leaks and allows multiple event functions to be executed BEFORE the document.body or document.images are fully loaded, as well there is no need to write those functions in a separate script and/or to put inside the ending BODY tag! – instead, all should be in ONE script before ending HEAD tag:

see the working test page * http://arieslink.sitebar.org/onload-1.html (the script there is internal, look at the source code, minimal changes were done to Jesse Skinner’s work, based in its turn on yours, but all errors and warnings were eliminated)

Best regards, Vlad

  • Comment by: Vlad Kout
  • Posted:

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

Was any of the variations posted ever determined to be THE complete, universal solution?

  • Comment by: Piz
  • Posted:

Interest in this topic has certainly been enduring.

I’ve been using variations on the methods posted here for a while now and have a fairly good cross-section of other people’s sites using and thereby testing these variations. IE of course has been the big challenge. Using document.write gave grief in a few instances, from outright failing to having the load routine take 2 minutes before firing. I had better luck with Diego’s doScroll technique but found that gave me trouble too. If I didn’t wait until document.readyState === ‘complete’, doScroll would succeed too early – before all the anchors were present in the DOM. But, at least on some pages, document.readyState was not complete until after all the graphics loaded – too late for my purposes. The best solution for IE appears to be waiting for the body element to be not null.

Synthesizing the wisdom from previous posts and doing extensive tests of some of the alternatives, the following seems to be very effective and reliable.

/*@cc_on
// Internet Explorer
	(function() {
		if (document.body !== null) return init();
		setTimeout(arguments.callee, 50);
	})();
/*@if (false) @*/
// Safari, Chrome, Konquerer
if (/Apple|KDE/i.test(navigator.vendor)) {
	(function() {
		if (/loaded|complete/.test(document.readyState)) return init();
		setTimeout(arguments.callee, 50);
	})();
// Mozilla, Opera9+
} else if (document.addEventListener) {
	document.addEventListener('DOMContentLoaded', init, false);
}
/*@end @*/
  • Comment by: byron
  • Posted:

A small correction to the comments in my code block above (too bad this board doesn’t allow edits of posts):

Chrome uses the DOMContentLoaded section, along with Mozilla and Opera9+. All browsers based on WebKit version 525 and greater can use DOMContentLoaded, but it doesn’t seem worth the effort to version detect those away from the readyState function.

  • Comment by: byron
  • Posted:

First, apologies for the serial postings.

The IE part of the code I posted above has proven to be < 100% dependable. We have found a validly constructed page where document.body exists, and yet getElementsByTagName returns no anchors.

Summing up the IE challenge:
Initialization event behaviours and order vary greatly among different pages.
Dean’s document.write of the deferred script has given problems on some pages causing a consistent > 60 sec delay.
Confirmed as Dean reports that document.readyState is unreliable. We have seen cases where it was “complete” while document.body was still null, and other cases where it was not complete until after all images on the page were loaded.
doScroll has been seen to succeed while document.body is still null.
And as already mentioned, document.body can be non-null prior to the DOM being available.

One solution that so far has tested 100% ok is to combine a test for both document.body and success of doScroll. So far, this combination has provided perfect DOMContentLoaded emulation on all pages tested. The final complete code looks like this:

function init() {
	if (arguments.callee.done) return;
	arguments.callee.done = true;
	// do your thing
}
if (document.addEventListener) {
	document.addEventListener('DOMContentLoaded', init, false);
}
(function() {
	/*@cc_on
	if (document.body) {
		try {
			document.createElement('div').doScroll('left');
			return init();
		} catch(e) {}
	}
	/*@if (false) @*/
	if (/loaded|complete/.test(document.readyState)) return init();
	/*@end @*/
	if (!init.done) setTimeout(arguments.callee, 50);
})();
_prevOnload = window.onload;
window.onload = function() {
	if (typeof _prevOnload === 'function') _prevOnload();
	init();
};

This uncommented code should be fairly self-explanatory. Basically, we are trying each method of dom-ready initialization that might work and letting the first one that fires win. AddEventListener is used for all browsers that have it. The IE code is targetted with conditional compilation. The cc “if (false)” keeps IE away from its unreliable setting of document.readyState. And the the check for !init.done is there to prevent the possibility of infinite setTimeouts. Finally onload is also set as a last resort.
One plus of this code is that there is no browser-sniffing (other than usage of IE conditional comments).

I hope someone finds something of value in this long-winded post.

  • Comment by: byron
  • Posted:

[…] alternative Inhalt via DOMScripting ersetzt. Dafür muss man FlashReplace entweder innerhalb einer window.load-Lösung aufrufen oder aber das Javascript nach dem alternativen Inhalt […]

@ byron, and really the rest of the contributing posters here

nice! i’m using the final handler you submitted, and it’s excellent! well for my purposes anyway. good to see that after 200 posts and 2 years there is somewhat of a final solution.

thanks all!

  • Comment by: bloodgleam
  • Posted:

I’m experiencing a strange issue in Internet Explorer 7 – document.readyState returns “interactive” after window.onload fires. Has anyone come across this behavior before?

  • Comment by: DV
  • Posted:

[…] milliseconds. Yet another alternative would be to employ DOMLoad, but you’d have to include extra javascript to get a cross browser friendly […]

[…] mit window.onload gibt es da so einige Probleme, siehe z.b hier Dean Edwards: window.onload (again) . Mit einem Framework deiner Wahl (jQuery,Prototype,MooTools,Dojo,…) kannst du das vereinfachen, […]

[…] famous method to catch DOMReady event in IE6/7 is utilizing script defer attribute: /*@cc_on @*/ /*@if (@_win32) document.write(“<script id=__ie_onload defer […]

I want to keep a header java script in a separate file and have this script loaded to different pages when the page opens, How do I do that? http://www.casinoexperte.eu

  • Comment by: anthony
  • Posted:

The last part of Byron’s script (the 4 or 5 lines in comment 201 checking for window.onload) generate “error on page” in various exporers.

I solved it by just changing that part to: window.onload = init; … which makes the error go away.

Does anyone know what exactly causes explorer to misread Byron’s code, and what functionality I might have lost by simplifying it like this?

Thanks for any feedback!

  • Comment by: bloodgleam
  • Posted:

[…] 最初的完整解决方案:http://dean.edwards.name/weblog/2006/06/again/ […]

Sorry if this sounds uber-newbie but.. why not just do a

document.write("<script>initMyClasses();<\/script>");

right before the closing </body>, which would still wait for the DOM to be loaded but not the images & other external objects ?

  • Comment by: Antoine
  • Posted:

I ran into a slight problem with the implementation for the IE 6 window.onload. If you are in a secure page the src=javascript:void(0) is seen as a unsecure external resource, thus the IE warning about secure / insecure items. Unfortunately I can’t get my users to dump IE 6 so I will have to figure out a way around it. Any suggestions?

  • Comment by: Michael Holly
  • Posted:

[…] 首先是由Dean Edwards编写的方法(http://dean.edwards.name/weblog/2006/06/again/): […]

  • Pingback by: Anonymous
  • Posted:

It’s been a long time since I’ve checked in to this thread. Since posting my implementation of the most reliable approaches presented here, I’ve modified it slightly.

First a simplification: Since we have to wait for the body to exist, we can do this by calling doScroll against the body, thus avoiding the dynamic element creation.

Second: doomgleam – The only reason I went with the DOM1 event window.onload is because I had a version of Safari that simply would not fire the DOM2 window.addEventListener ‘load’ event. This may in fact have been a beta release and I think it is safe to count on the DOM2 events.

Strictly speaking, the window.onload event is not really part of this early dom loading technique. It is included in the solution because in IE window.onload can, and often does, fire before the DOMContentLoaded handler on quick-loading pages (because of the timer used for IE).

The final tried-and-true simplified version is:

function init() {
  if (arguments.callee.done) return;
  arguments.callee.done = true;
  // do your thing
}

if (document.addEventListener) {
  document.addEventListener('DOMContentLoaded', init, false);
}
(function() {
	/*@cc_on
	try {
		document.body.doScroll('up');
		return init();
	} catch(e) {}
	/*@if (false) @*/
	if (/loaded|complete/.test(document.readyState)) return init();
	/*@end @*/
	if (!init.done) setTimeout(arguments.callee, 30);
})();

if (window.addEventListener) {
	window.addEventListener('load', init, false);
} else if (window.attachEvent) {
	window.attachEvent('onload', init);
}

A final observation. This code tests fine through to IE8, but of course things might break with IE9. We are counting on doScroll throwing an error before the DOM is ready, but this is arbitrary behaviour that may change on IE9. Hopefully, the DOMContentLoaded event will be handled natively.

  • Comment by: byron
  • Posted:

[…] (when DOM tree is ready to be adjusted – so this event is sometimes called onDOMready). There are several articles and approaches about its cross browser implementation. But it can be efficiently countered (and we […]

Byron, I really appreciate you coming back after all this time to keep updating the thread. I’d buy you lunch if I knew how.

You don’t by any chance also know of an updated addEvent() cross-browser working script do you? I keep finding things from 2006 and older.

  • Comment by: Erik Eckhardt
  • Posted:

I have bad news about the solution provided by Matthias Miller. I implemented the solution and it works, BUT, in a secure (SSL) environment gives “Do you want to view only the webpage content that was delivered securely?” If you add “https://” in front of javascript:void() the problem goes away. A modification for SSL is needed.

  • Comment by: Jerold Anderson
  • Posted:

Hi there – I’m suffering from this window.onload problem, but the solutions above still do NOT appear to work in IE. I’ve tried IE7 and IE8. The solution works fine in Firefox (3.6.3); Chrome (4.1.249.1064); Safari (4.0.5); Opera (10.10).

I should say that I’ve used ALL the “complete” solutions above down to the last one in comment 213.

My two versions of IE are 7.0.5730.13 and 8.0.7600.16385 – and produce an incredibly helpful message of unknown runtime error.

Someone suggested using the JQuery construct

$(document).ready(function(){ init(); })

But that seems to cause more problems in that it states $(document) to be null (Firefox/Firebug).

I am no JS expert – in fact I hardly know it and I’m just using some functions I found on the web that do what I want – so, please don’t blind me with any JS technical pieces as they will go straight over my head.

What I’m trying to use is the modal dialog box from About.Com – http://javascript.about.com/library/blmodald1.htm

Also, I use the Videolightbox – http://videolightbox.com and the TinyScroller – http://www.leigeber.com/2009/12/slideshow-script/ and TinyBox – http://www.leigeber.com/2009/05/javascript-popup-box/

As well as a few hand crafted scripts of my own – that are just fairly simple – to show/hide things as the mouse moves and to count characters and limit them in input fields.

It would appear that Videolightbox also uses part of Jquery – so I’m unclear if that is causing some knock on problems in the JQuery solution that I was passed.

What the mdb script is trying to do is to create two div elements in the DOM that are essential for the thing to work. If I put them in a simple test page – then everything works fine. But, if I put them in the real page (has images and the like) then they never get created.

Any help gratefully received.

  • Comment by: highlawn
  • Posted:

There’s a slight problem….

When you call location.replace when the page is loading, at some point after the deferred script is added to the page, the init routine will still run and any ‘getElementById’ calls will return null because the document hasn’t loaded.

Always make sure you check for the presence of elements — otherwise you might get a JS error after replace is called, but before the new page loads.

Also, re#217 — better solution for SSL is to use ‘src=//:’ which works with both SSL and non-SSL.

  • Comment by: Tyler
  • Posted:

I think Byron’s function in comment 213 is the best solution around — it’s the one I always find myself using. I’ve been looking around and I’ve noticed that a lot of the modern browsers support DOMContentLoaded (Firefox 1.5+, Safari 3+, Chrome, Opera 9+ according to Jakub Pawlowicz http://perfectionlabstips.wordpress.com/2008/12/01/which-browsers-support-native-domcontentloaded-event/ ) and Microsoft are thinking about adding support in IE9 ( http://blogs.msdn.com/b/ie/archive/2010/03/26/dom-level-3-events-support-in-ie9.aspx ) — they even support addEventListener and passing events through arguments. Hopefully in a couple of years, all we’ll need is:

document.addEventListener('DOMContentLoaded', function(e) {alert('Life is good');}, false);

[…] for everything to load before scripts could run was now an unacceptable delay. Around 2005, a variety of solutions emerged; the most convenient and straightforward of these have now become standard approaches, […]

putting the scripts at the bottom of the page, will defer the domcontentready event . This may cause a delay in initializing event handlers , if i have written handlers in ondomcontentready event handler.

  • Comment by: rajkamal
  • Posted:

[…] At this point I found IE wouldn’t attach load events to script tags. I dimly recall something about this from Dean Edwards’ posts on window.onload. […]

[…] […]

DOMContentLoaded is now supported by Internet Explorer 9, you can test drive it at: http://ie.microsoft.com/testdrive/HTML5/DOMContentLoaded/Default.html

[…] in pre IE9 browsers, and the page relied on one of two solutions: “doScroll()” or “script defer” to approximate when the event has been reached. Once the event was fired, JavaScript on the page […]

[…] Dean Edwards: window.onload (again)Is there a nice Dean Edwards Seal of Approval wrapper fucntion for all the onload code per chance? … Pingback by: Dean Edwards: window.onload (again) at The Hero Dies in This One… […]

[…] ObjectSwap就是基于这样的原则,并且我认为将会是未来flash嵌入库的榜样架构。不幸的是,ObjectSwap注意力主要集中在解决自动激活上,所以在解决版本检测和诸如IE上的流bug和老Safari的param bug这样的标签支持问题上,就没有多大作用。另一个可以提高的地方在于——所有低调库都应该加以注意的是——性能。使用onload事件的话,你的 DOM相关代码只会在你整个页面,包括页面上的元素都加载完成后才执行。用DOMContentLoade事件来代替会更好,那就会在DOM加载完后立即触发。但是DOMContentLoade并没有被所有浏览器支持,你也许可以用这个方案代替。 […]

[…] […]

[…] 那,如何检查DOM何时加载完成呢? 这种方法比较冗长,具体请见Dean Edwards的博客。 […]

Comments are closed.