Sunday, 18 October 2009

Using Document onLoad instead of Window onLoad

Why we should use DOMReady instead of window.onload

I know this is a topic that has been extensively covered by many developers but I thought I would give an example of a good reason to use a function that tests for the DOM to be ready to start running your JavaScript functions instead of the window.onload event to fire.

JQuery users will be used to writing code like this:
$(document).ready(function() {
alert("DOM is ready")
});
Which will try to fire when the DOM is ready and if not when the window is ready. If you write code like this:
$(window).load(function() {
alert("Window and all Iframe and Image content is ready")
});
Then you are waiting for the window to be ready to fire your functions which maybe a safer option if you are wanting to manipulate image or iframe content or content that has been loaded externally but on most occasions you would want to use a onDOMReady function like the one below which I have taken from code I use on this site. It is pretty similar to how most major frameworks including JQuery have inside their own cross browser DOMReady functions, however I am still testing for old Opera and Webkit users whereas JQuery does not do this anymore.

Also I have had to take the compressed version and then unpacked it due to not being on my own PC tonight due to problems with my leg but the code shouldn't be too bad and shouldn't be treated as copy and paste code but more like pseudo code.
onDOMLoad: function () {
// only look for modern browsers that haven't spoofed their agent (not perfect check but then its their own fault!)
if (Browser.w3cDOM && !Browser.spoof) {
// For old webkit and opera and KHTML we use a timer to test for readystate
if ((Browser.webkit && Browser.webkitversion < 525) || Browser.khtml || (Browser.opera && Browser.version < 9) && document.readyState != "undefined") {
PageLoader.DOMTimer = setInterval(function () {
if (/loaded|complete/.test(document.readyState)) {
PageLoader.RunDOMLoadFunctions()
}
},
10)
// For other standard compliant modern browsers apart from IE we can use the standard DOMContentLoaded function
// making sure to remove the anonymous function straight away.
} else if (document.addEventListener) {
AddEvent(document, "DOMContentLoaded", function () {
RemoveEvent(document, "DOMContentLoaded", arguments.callee);
PageLoader.RunDOMLoadFunctions()
},
false)
// For IE (and Opera which is why we do this last) we try two methods the first one can sometimes
// fire very late on in the day.
} else if (document.attachEvent) {
AddEvent(document, "onreadystatechange", function () {
if (document.readyState === "complete") {
RemoveEvent(document, "onreadystatechange", arguments.callee);
PageLoader.RunDOMLoadFunctions()
}
},
false);

// We also do this trick by Diego Perini to continually check for DOM readiness by
// checking for an error which comes from the doScroll call. Once there is no error reported we
// know the DOM is ready. This doesn't work for Iframes note the window comparison.
if (document.documentElement.doScroll && window == window.top)(function () {
if (PageLoader.DOMLoaded) return;
try {
document.documentElement.doScroll("left")
} catch(e) {
setTimeout(arguments.callee, 0);
return
};
PageLoader.RunDOMLoadFunctions()
})()
}
};
// Always call a window onload function to run anything not fired already.
PageLoader.AddWindowLoadEvent(function () {
PageLoader.RunDOMLoadFunctions()
});
return true
}
}


A good reason for doing this came to light very recently with a system we are working on updating the folder structure that holds site related files such as images, banners, logos etc that are loaded and used on the site. Because we are halfway through the process the folder structure has changed which means when a page loads all the images appear broken as they haven't been moved to the new structure yet. This is obviously on a development server!

However on a few pages it has been noticeable that as the page is loading you may start filling in form fields for input and then suddenly halfway through your focus is removed from the field you are currently on and taken to another field, usually the first field on the page. The reason for this is that the code being used to run the set focus() is being called by a function that is using a window.onload event rather than an onDOMReady function.

This doesn't happen in all places and I haven't fully looked into it to see if its mainly an Internet Explorer problem, which has well known issues with firing functions between the loading of the DOM and the Window, or an issue where the DOMReady doesn't fire for whatever reason and then falls back to window.onload. However its an example of the problem that when using window.onload the browser will wait until all images have been loaded before firing and due to the number of broken banner logos and images this is quite a number of failed checks which is why the delay seems so long.

A quick thought has been to add an extra fallback call in between the DOMReady and window.onload which would fire once the Body has loaded. This could be similar to a function below of mine which polls the DOM looking for the Body element to be ready before running the desired function. Again this is taken directly from unpacked code with comments re-added so treat as pseudo code.
onBodyLoad: function (fn) {
// Check the function is a function e.g typeof(fn)=="function"
if (S.isFunction(fn)) {
// if we haven't already called this function
if (!this.Body[fn]) {
// if the body has already loaded and we can get a reference to it
if (P.BodyLoaded || S.getBody()) {
// Call the function
fn.call();

// Set some flags so we know we have called the functon and also
// that the body has loaded in case other functions (window/dom) ready haven't
P.Body[fn] = true;
P.BodyLoaded = true
}
} else {
// call a timeout passing in the function to check again in 50ms
setTimeout(function () {
P.onBodyLoad(fn)
},
50)
}
}
}


Depending on what you put between the close BODY tag and the close HTML tag which shouldn't be much this might help give the effect of a DOMReady if for whatever reason the original code doesn't fire.

1 comment:

Rob Reid said...

I have just noticed how the code highlighting script I am using at the moment is not doing such a good job at interpreting comments when they contain keywords.

I haven't had a chance to look into it but if anyone has any other good recommendations for free code highlighter scripts that I could use then please let me know!