dean.edwards.name/weblog/2005/02/order-of-events/

Order of Events

PPK’s article for alistapart entitled "JavaScript Triggers", describes how to separate behavior from content by attaching event handlers at page load time. Jim Ley points out that the window’s onload event is sometimes too late to do this. Although I think he is right, it is only a problem for pages with lots of graphical content.

The problem is that the window’s onload event fires after all page content is loaded. That includes images and other binary content. If a page has lots of images (or one big one), then the onload event may not fire for several seconds after the HTML content is visible. If a menu is animated by JavaScript, then it would not be active immediately (unlike a CSS based menu).

Internet Explorer (and Opera) provide additional properties and events that provide a better indication of the state of the document. On these platforms, a document’s readyState is set to "interactive" when the document’s HTML has been loaded and parsed. This is the event you really want! It occurs immediately after the DOM has been built and doesn’t wait around for images to catch up.

A while back I did some experiments to see the order of events for page load. Bear in mind that Mozilla browsers don’t support onreadystatechange, so the results won’t mean much on that platform. Internet Explorer has the most revealing results.

Update. I’ve posted a follow-up to this post with more information on DOMContentLoaded and a way to imitate this event for Internet Explorer.

Comments (28)

Leave a comment

This is interesting; I’ve come across a related issue while using your very useful “mozilla behaviours” XML-translation-metamorphosis script. I noticed that at some point you fire off the event “ondocumentready” immediately because Mozilla doesn’t support it. I have an htc file that colours table rows in an odd-even pattern, and the event is fired “ondocumentready” – with the side effect that in Mozilla, for large tables in slow connections only part of the table gets painted (the “for-each-row” loop starts before all rows are there).

I wonder if you have any clever suggestions?

  • Comment by: Nelson Menezes
  • Posted:

I wonder if you have any clever suggestions?

Hi Nelson. I was thinking of faking the ondocumentready event for Mozilla by using XBL somehow but I haven’t had a chance to experiment.

There is a new version of moz-behaviors awaiting release. It has better support for ondocumentready and oncontentready but it is still not perfect.

If anyone knows how to detect when the document has finished parsing on Mozilla please let me know! :-)

  • Comment by: -dean
  • Posted:

Dean,

If anyone knows how to detect when the document has finished parsing on Mozilla please let me know!

There was some discussion about this on the Greasemonkey mailing list.

Aaron Boodman (the author of Greasemonkey) said:

I finally discovered DOMContentLoaded event. It’s an undocument, proprietary event that Mozilla throws and which is accessible from script when, um, the dom’s content is loaded (that is, before all the images and what not have loaded).

Is this what you are looking for?

Prakash

P.S. Thanks for all the work you’ve done.

  • Comment by: Prakash Kailasa
  • Posted:

Thanks Prakash!

That’s exactly what I’m looking for! :-)

  • Comment by: -dean
  • Posted:
Hi, did you get DOMContentLoaded working? I am having a hard time getting it to work
  • Comment by: Abdul Rahman
  • Posted:

Abdul, try this:

function test() {
	alert("Success!");
};
document.addEventListener("DOMContentLoaded", test, false);
  • Comment by: -dean
  • Posted:
Thnx dean, work like a charm!
  • Comment by: Abdul Rahman
  • Posted:
Am I missing something here. If the “interactive” state means the DOM has been loaded and parsed surely I should be able to retrieve a document’s link count when I call a function to do this. However this always returns zero suggesting this isn’t the case.
if (document.readyState == "interactive"){
	countLinks();
}
If I call the same function from an onreadystatechange event handler the function now only fires once the document’s state has changed to “complete” (which is too late) defeating the purpose of the ‘preload’.
if (document.readyState == "interactive"){
	document.onreadystatechange = countLinks;
}
Which is a shame since the Mozilla equivalent works a treat.
  • Comment by: Paul Mills
  • Posted:
Yeah. I’m starting to mistrust this readyState thing myself now… :-( If you really need to trap this event on IE then use a DHTML behavior attached to the document body and trap the ondocumentready event.
  • Comment by: -dean
  • Posted:
Dean, Thanks for the suggestion. It turns out that using ondocumentready via a behavior is the solution to accessing the DOM this way and not readyState/onreadystatechange. Unfortunately for me the server hosting the site in question isn’t set up for htc files so I’m stuck with the existing onload.
  • Comment by: Paul Mills
  • Posted:

[...] t approach is when working with other browsers, although I’m probably just repeating Dean’s work. We’d all drunk enough by then that the discussion of Design [...]

DomContentLoaded and ondocumentready attached via an htc would seem to be half the battle, but…. Have any ideas on detecting support for DomContentLoaded? The second one is easy enough to handle by testing for document.body.addBehavior, but I can’t figure out any way to detect the first.

I’d rather not have to fall-back to my polling alternative when there is such a suitable event, but I’d have to for cross-browser support if I couldn’t figure out how to detect it. (If it isn’t obvious I’m working on a xbrowser method of handling a DomContentLoaded-esque event – and yes, I know I’m not the first).

  • Comment by: Tim Connor
  • Posted:

Tim – I intend to publish an update to this post fairly soon. I’ll answer your question and show some alternative techniques too. Stay tuned!

  • Comment by: -dean
  • Posted:

Cool, thanks Dean. I’ll hold off on getting too fancy until then. ;)

I guess for now I’ll stick with your cssQuery fired off by either brothercake’s DOM function (maybe passed a silly id=”lastChild”), the basic xbrowser onload binding, or whatever the simplest use of domcontent and readystate falling back to the onload I can come up with, for my event-binding needs.

I’m excited to see what you’re going to post on this (well, I have been since hearing you were planning a summary of it all after @media, but decided to start looking into it more in impatience :) ) and how slick we can get armed with that knowledge.

  • Comment by: Tim Connor
  • Posted:

Any news on this? I’m still waiting for the best and most concise ‘onDOMLoad’ event function – if anybody is the man to write it then it’s you Dean. I’ve tried using a setTimeout to check for the DOM tree and it works pretty well (even in Mac IE), however my function is simple a replacement for window.onload and isn’t that clever at all (plus it seems to fire the onload event too many times).

I tried to post the js code but it kept saying ‘not well formed xhtml’ even tho it was in in pre tags

  • Comment by: Jon B
  • Posted:
// to use simply assign an onload property like so: DOM.onload = fnc or DOM.onload = function(){ code }

DOM = {
	onload: function(){ alert('DOM Says Hi!') },
	check: function(){ if(document && document.getElementsByTagName('BODY')){ DOM.onload(); clearInterval(DOM.interval) }},
	interval: setInterval('DOM.check()',10)
}
  • Comment by: Jon B
  • Posted:

OK guys, I’m moving this nearer to the top of my “To Do” list. It will probably be cross-posted on the WaSP DOM Scripting site too. I just got back from a mini-break in Edinburgh and have some catching up to do. But there is clearly a demand for this information so I’ll do my best to satisfy demand.

  • Comment by: -dean
  • Posted:

Of course, this will depend largely on what you end up posting, but the more I look into it the more I’m leaning towards just making a constructor (and the .htc of course) that uses DomContentLoaded and ondocumentready semi-blindly (with the appropriate checks to not throw an error) and let’s everyone else deal with waiting on the onload. Do some fancy footwork about how you attach all of the above through a consistent point, and then just keep track of wether this has been run yet.

If either of the pre-onload calls went through, you’ve marked your uber function as already run and when the onload fires it just exits (and of course, marks it as run in case something causes either event to fire after the onload – like maybe Safari supporting DomContentLoaded later, but in a different way).

  • Comment by: Tim Connor
  • Posted:

If I may be so bold to offer up a solution I released a few months ago. It doesn’t match up exactly with what some of you are looking for, but may be of use to others.

onDOMload is similar to brothercake’s DOM function, but looks for ids/tags/classes of elements on which to run the desired code. So it can be run on a single element (id) or multiple elements (class/tag).

Hope this is helpful to some, and I’m not out of line posting to my own stuff. Found this discussion by searching for “ondomload” in Google, which found Jon B’s mention of the word/concept in post 15… so figured I’d offer up my script of the same name.

But I am curious to see what dean is able to come up with as well :)

Hi, Dean

the detection of readyState works for me on http://www.hedgerwow.com/360/dhtml/before_onload.php.

May I ask that what`s your concern about the latency of detection of readySate in this case?

Thanks for your help.

Regards.

  • Comment by: Hedger
  • Posted:

Hedger – I don’t know why readyState is unreliable. The only thing I can say is that sometimes it works and sometimes it doesn’t. The complete state is the same as window.onload and the interactive state is usually OK for small pages but doesn’t always imply that the DOM is complete. This is especially true if you are using document.write to generate content.

  • Comment by: -dean
  • Posted:

Really works well. Thanks!. But IFRAME loaded very slowly in Mozilla.

  • Comment by: Ashok
  • Posted:

[...] We’ve been doing cool stuff on the web for years with no where to post about it, but now welcome to our blog. After watching all the cool developments of the last year or two and discussing them on other people’s blogs I thought “why not have a blog of our own?” [...]

[...] Ever since the PPK mentioned something in his blog about a way to solve it in IE and Mozilla(maybe Safari) in the @media event entries a number of us have been very excited. We’ve been contemplating the best way to use this knowledge involving complicated schemes requiring behaviors in IE (.htc files) and waiting for the mysterious “more details to come” [...]

Hi, DOMContentLoaded not works when the response is a application/pdf file. What I Do?

  • Comment by: Marcel Stange
  • Posted:

[...] [更新日志] 070520:Norman 君给出了 Dean Edwards 等关于 onDOMLoaded 事件的跨浏览器解决方案…… 其实 onDOMLoaded 是 DOM 被载入(图片尚未载入)状态下的一个“不存在的”事件…… 由于不同浏览器的支持度不同…… 所以需要进行 Hack …… 所幸 Dean 和其他一些朋友反复 究后给出了解决方案…… 在此表示感谢…… 对于缩图函式本身的改进就是合并了上个版本的两个不同模式(快速和平缓)…… 所谓快速模式是用了 while 循环…… 速度快但是容易 成假死…… 所谓平缓模式则用 setInterval 函式来解决上述问题…… 可惜 setInterval 有最短周期限制…… 所以速度比较慢…… 从快速到平缓模式的切换临界值我设置为 500 …… 也即当一个页面的图片达到 500 时便启用平缓模式来缩图…… 大伙儿可以 据自己需求更改…… 具体请看 resizeImgs 函式的第二行…… 070516:感谢 Norman 君在提升代 性能上的建议…… 将遍历所有元 更改为遍历图像数组…… 至于 Norman 所提议的在图像读取之前即进行缩图的功能…… 未能实现…… 一来是 为 Firefox 并不支持 onreadystatechange 事件(只有 onload ?)…… 二来则在测试中发现 IE 下在 interactive 状态下调整图像大小也需要刷新以后才能执行…… 也就是说都得等到页面 载完毕(或者 载一次)后才能进行操作…… 恕鄙人才疏学浅…… 如有解决方案望留言教授…… [...]

[...] 参考链接如下(其实是前后相继的三篇不断补充的文 ): 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 [...]

In Internet Explorer 7, when I do an alert(document.readyState) through window.onload, it shows “interactive”. Has anyone experienced this before?

  • Comment by: DV
  • Posted:

Leave A Comment

Line and paragraph breaks automatic, email address never displayed. Some HTML allowed.