Exceptional performance with a flavour RSS

@3rdEden on twitter

Archive

Jan
2nd
Fri
permalink

Reinventing the wheel, cross browser DOM ready

There are different techniques to have DOM ready functionality. Some techniques are faster,smarter than the other (or a combination of both). Having it working in various of browser has always been an issue, with HTML 5 they introduced a new event called DOMContentLoaded.

Its a great step to have cross browser DOM ready functionality, but there will always be browsers who doesn’t have this implemented *cough* Internet Explorer *cough* and also the older browsers that some people are still using. So I think it will be safe to say that we can use the DOMContentLoaded completely crossbrowser in the year 2020.

That means we are still stuck for about 10 years using multiple techniques to have full cross browser support. Or maybe not?

I always wondered if there was room for improvement, when I build the SpryDOMready widget for the Adobe Spry Framework I saw the horror of having cross browser support. For example the “fastest” way in Internet Explorer still requires doing a document.write();


document.write("<scr" + "ipt id='unique' defer " + "src=//:><\/scr" + "ipt>");

So I started some different techniques that i was thinking about, and they all sucked. Really it was awful, than I remembered something “KISS (Keep It Simple Stupid)”. After a some code fiddling I thought of document.body. Would browsers be so smart to have that disabled until you can actually access the DOM? A small test showed me that I was right, document.body was not accessible until the DOM was loaded. To verify that we really have access to the nodes we needed to have a check, node.lastChild provided the solution.


(function(){
    if(document.body && document.body.lastChild){
        domReady();
    } else { 
        return setTimeout(arguments.callee,0);
    }
})();

It amazed me that it actually worked, and after some test it showed how accurate it was. It did get executed before the onload event, the DOM elements where accessible and it didn’t require any hacks. I was primarily testing in Internet Explorer so I compared my script with my other DOM ready script. It was actually slower, it worked, but it was slightly slower. Initially I didn’t bother checking the script in FireFox. Why would I? They already have native functionality for it (DOMContentLoaded event) so no way a little piece of custom code would be faster than that.. Or would it?

So I started up FireFox and checked if it made any difference, this little piece of code actually beat the native implementation of DOM ready. It was 50~ milliseconds (average) faster. Not only FireFox but also Opera showed speed improvements compared to the their native DOM ready implementation.

After allot testing I decided to release this new functionality in to my latest DOM ready widget for the Adobe Spry Framework.

Download location: http://www.spry-it.com/builds/SpryDOMReady.js
Working preview: http://www.spry-it.com/examples/SpryDOMReady/

(Ignore the design, its old but it simulates a good “real” website so its a great place to host a working preview.) At the preview you can also see the execution time snapshots. You will also see a working standalone version of this DOM ready script. And jQuery’s DOM ready implementation just for reference.

While I was typing this something hit me, this DOM ready functionality is actually completely cross browser. I tested it in Internet Explorer, FireFox, Safari / Webkit / Chrome and Opera. So not only is it faster than the DOMContentLoaded but it also works in all browsers.

It might not be fastest solution for each browsers, but if you are willing to give up a few milliseconds (5/10~) you can have a small cross browser DOM ready solution.

Here is the complete working cross domain, standalone version of the script, released under GPL/CC


/*
Copyright: 
    Arnout Kazemier ( blog.3rd-Eden.com )
Version:
    1.0.0.0
License:
    GPL/CC
*/
var fnQue = [];
var domIsReady;
function domReady(fn){
    if(domIsReady)
        return fn();

    if(!fnQue.length){
        function ready(){
            if(domIsReady)
                return;
            domIsReady = true;

            var i = fnQue.length; while(i--)
                fnQue[i]();

            fnQue = null;
        };
        (function(){
            if(document.body && document.body.lastChild){
                ready();
            } else { 
                return setTimeout(arguments.callee,0);
            }
        })();
        window.onload = ready;
    };
    fnQue.unshift(fn);
};

If you need with implementation, or have question feel free to comment, email or tweet me.

Comments (View)