onDOMReady: No Browser Sniffing!
For the most part, I’ve typically relied on window.onload for my various projects; its simple, cross-browser, and the alternative onDOMReady functions were unreliable due to their utilization of browser sniffing in some fashion. However, the problem with window.onload, as I’m sure you’re all aware, is that it doesn’t fire until the entire page has loaded including all images. This can make for a usability nightmare for any page with a lot of images and/or anybody with a slow connection. If your page relies on JavaScript then there could very well be an extended amount of time whereby the user is interacting with the page and it hasn’t yet been initialized. An onDOMReady-related function is the more modern approach, allowing you to define a function that will execute as soon as the DOM is in a usable state, that is to say structurally complete. The main obstacle now is to create a solution that is not dependent on browser detection.
Features
The function itself works similar to the other solutions available around the web including those found in the various Javascript libraries. However, rather than separating each routine via browser detection, I simply use one function to parse the document’s current state and determine if the DOM is ready for manipulation. For Firefox and Opera a simple check of event type will determine if it is DOMContentLoaded. Safari and IE will check against the document’s ready state, and alternatively IE will routinely try to scroll the document left, if successful – the DOM is ready. Finally in case all else fails, the onload event will bring up the rear. Upon completion, the supplied function is called and all event listeners are removed and nulled out to prevent memory leaks. Test it out here.
function onDOMReady(fn, ctx){ var ready, timer; var onChange = function(e){ if(e && e.type == "DOMContentLoaded"){ fireDOMReady(); }else if(e && e.type == "load"){ fireDOMReady(); }else if(document.readyState){ if((/loaded|complete/).test(document.readyState)){ fireDOMReady(); }else if(!!document.documentElement.doScroll){ try{ ready || document.documentElement.doScroll('left'); }catch(e){ return; } fireDOMReady(); } } }; var fireDOMReady = function(){ if(!ready){ ready = true; fn.call(ctx || window); if(document.removeEventListener) document.removeEventListener("DOMContentLoaded", onChange, false); document.onreadystatechange = null; window.onload = null; clearInterval(timer); timer = null; } }; if(document.addEventListener) document.addEventListener("DOMContentLoaded", onChange, false); document.onreadystatechange = onChange; timer = setInterval(onChange, 5); window.onload = onChange; };
I also made a similar version that utilized a defer script for IE and worked just as well, however due to a fundamental flaw in the implementation I recently became aware of, I decided the current version as it is now would suffice. In any event, there you have it – a simple, clean, and reliable snippet of code to handle all your onload handling in just 40 lines of code.
Download
View full documentation and download the source for this project on Github.
This seems like a great idea. I’m surprised nobody else has commented, as this seems to be one of the few standalone solutions available. I’m wondering, is this still the current code you’re using, or has it been tweaked? Any problems with older browsers?
@Leo
The code is still the same but to be honest, I haven’t looked at it in a while, so perhaps some tweaking is in order. Although I had initially created an alternate version which employed a deferred script instead of Diego Perini’s doScroll hack for IE.
Now that I think about it, I had planned on adding the ability to call the function multiple times with multiple callbacks rather than just once.
As per browser support, I have tested the method to success in IE6+, FF2+, Opera 9+, Safari 2+, and Chrome 1+ but haven’t been able to get my hands on any older browsers so I can’t be too sure. However, the function should degrade gracefully to window.onload in any browsers that do not support the more advanced methods the script utilizes thanks to the lack of browser detection.
Looks like this could be a nice little function. Sounds rather like Dean Edwards’ method (see for instance http://peter.michaux.ca/articles/the-window-onload-problem-still), but I do like the idea of mopping up all the event listeners at the end. One question though, and probably because I just don’t understand it properly, but how does this function fire? I ask because all the event handlers and calls to fire the function actually reside within a function definition, there seems to be no trigger outside this? I’ve tried running your (fully documented) version myself, and things will only fire if, say onload, is placed outside the function (ie globally). What am I missing?
@Julie
If I am understanding your question correctly, the function is able to fire because it is still within the context of the onDOMReady function, any function definitions (fireDOMReady) that reside within that context are part of the scope chain and therefore have access to all variables at any level equal to or higher than its own. The event handlers hold a reference to fireDOMReady which in turn holds a reference to the user-provided function.
I am not to sure what your eluding to with this; “things will only fire if, say onload, is placed outside the function (ie globally).” But I can tell you, that onDOMReady can be called within any context, but it is important to note it can only be called once per page (I hope to upgrade this ability in the future to unlimited calls). It is typically best to pass the function directly to the method rather than a reference to avoid polluting the global namespace:
onDOMReady(function(){
//do something
})
I hope this answers your question!
Hi Ryan,
Many thanks for getting back so quickly, and yep you’ve sure answered my question! I hadn’t taken it in properly that you’re passing a function into the onDOMReady. I’ll have to delve into this in more detail, it’s a bit of a revelation to me, but sounds a great technique to keep things out of the global context. Sounds like you’ve really got something here
)
This is an excellent function which seems to work well. If you don’t mind I’d like to point to it as a recommended replacement for window.onload in the downloadable files accompanying my forthcoming book.
(Ooops, should mention the URL: http://pluginjavascript.com)
@Robin Nixon
I would be honored to have my work represented in a book. Thanks for the consideration!
[...] used this one for several of my architectures, which has the advantage of no browser-sniffing. http://ryanmorr.com/archives/ond…Insert a dynamic date hereCannot add comment at this time. Don't Miss New AnswersJoin [...]
Hi Ryan,
I was wondering if you are still thinking about upgrading this so that it can be used multiple times in a page. I’m using window.onload to manipulate several adsense units from the footer to different places in the page, and I use your plugin with one unit and it works great. I’d like to be able to use it with other units so that they can be loaded when they are ready, not when everything is loaded as my site contains lot’s of pics..
Any chances of adding this ability anytime soon?
Regards
XO39
@XO39
I actually updated this a long time ago in github but never wrote a blog post to reflect the update. You can find it here: https://github.com/ryanmorr/onDOMReady
In the future, check out https://github.com/ryanmorr for my most recent updates to this and other projects.
Thanks for you fast reply, Ryan.
So now all I have to do is only using it this way:
onDOMReady(function(){
//do something
})
As many times as I need? Or should I add/change something every time I use it in the same page?
BTW, I’ve bookmarked https://github.com/ryanmorr and certainly I’ll recheck it from time to time..
-XO
@XO39
You do not need to change a thing, just keep using it the same way as many times as you need.
OK, Thanks a lot Ryan.
I’ve just tested it and it works really well.
Thanks again
-XO
Hello,
Thanks for this, I’ve been using it for a while now for google adsense.
But recently I found an issue with it, it does not display google adsense in google chrome. Ads are displayed in ff, opera and ie but in chrome it has some issue, ads are displayed only first time you load the page, when you navigate to another page the space of the ads is there but there is not ads and they don’t show unless I refresh the page or even hard refresh, tried that with more than computer, not using this function ads are normally there, I was trying to figure why, then after testing the reason was this file, maybe chrome doesn’t like it. Can you fix this if you have time, please?
thanks again
Hi,
thanks Ryan this function is great and solved me big headache when dealing with user having slow connections and using especially IE or Chrome.
I will recommend this function.
Thanks