Parallel Script Loading with LABjs

Once a month, I lead the Austin JavaScript meetup here in Austin, Texas where we get together, talk shop, show demos of projects, and then have beers at the Draught House. After the first few meetings, Kyle Simpson (@getify) decided to help me out with providing content for the meetups and has provided loads of top-notch quality stuff from his talk on closures and the module programming model to some of his own projects like flxhr.

Recently, Kyle gave a talk on how to download scripts in parallel, something Steve Souders and a few others have discussed how to do effectively. Kyle wrote his own, JavaScript library/framework-agnostic library (for lack of a better term) to allow for parallel script downloading with our without blocking (think dependencies). He presented this project of his at our meetup in June, and thus LABjs was born.

Some History on <script> Tags and Performance

If you’ve ever been to a website like Mashable.com or Marketwatch.com or a slew of other sites that seem to take FOREVER to download even with a high-speed connection, then you’ve probably been the victim of multiple <script> tags blocking the page from fully downloading, and ultimately rendering. Sorry in advance for picking on Mashable, but the home page had a whopping 59 JavaScript requests when I went to it today so it is a perfect candidate for LABjs’s usage, or simply some JavaScript loading optimization of some kind.

Mashable's Insane Number of JavaScript Requests

So why is this bad? Well, <script> tags, when they download, block other assets from downloading to fully render the page. As a result, a user’s experience with the webpage is severely degraded as that page may take significantly longer to download and subsequently render because the browser is unable to download anything else while the <script> tag is “bogarting” the download queue. Steve Souders provides an excellent demo of this problem here.

Download Without Blocking

So how do we avoid or at least minimize the deterioration of the user experience from the blocking associated with multiple <script> tag requests? That is where LABjs comes in. LABjs, an acronym for “Loading and Blocking JavaScript”, uses well known, yet clever techniques for adding script tags in a manner that allows for non-blocking, parallel downloads.

A quick and easy example of this can be seen in the following code on this demo page.

Upon page load, a quick inspection of the Net tab in Firebug yields the following results:

Parallel Loading Without Dependencies

What should be noted here is how JavaScript files are being downloading not only with other JavaScript files, but images as well. The process could not have been simpler either.

<head>
 <script type="text/javascript" src="js/LAB.js">
 </script>
 <script type="text/javascript" src="js/load.js">
 </script>
</head>

In the <head> tag we only need to include the request to the LAB.js library file and then a file I called load.js which contains the following LAB API calls.

$LAB
.script("http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js", "http://www.google-analytics.com/ga.js")
.block(function(){
 pageTracker = _gat._getTracker("UA-3312370-2");
 pageTracker._trackPageview();
});

If you notice, I am making a call to the script() method passing in a request for the latest version of jQuery and the Google Analytics tracking script. The next is a block() method call that handles my Google Analytics tracking. Even though this example was to show a simple use of the LABjs API, I did add this block of code for my own personal tracking statistics. The block() method call is not required for this example to work.

Downloading with Dependencies

One of the best features that puts LABjs ahead of other methods and frameworks that produce similar results is that LABjs allows for you to load a file, say the latest version of jQuery, and then run some code that is dependent on that file’s existence.

An example of using LABjs with dependencies can be seen in the following code on this demo page.

Upon page load, a quick inspection of the Net tab in Firebug yields the following results:

Parallel Loading With Dependencies

What is important to note here is the times and places where global.js and the __utm.gif tracking image were downloaded: global.js is dependent on the jQuery library to execute some code and the tracking image is a result of the Google Analytics script executing the tracking code. These files were downloaded (and executed) after their dependent files were fully downloaded, the result of the block() method call. The ease of use and the robustness of dependency management coupled with non-blocking script downloads should be evident with this example.

The Conventional Route

Of course, I have to show the same page using the conventional approach of multiple <script> tags. The demo is here.

Upon page load, a quick inspection of the Net tab in Firebug yields the following results:

Conventional Method of Downloading JavaScript Files

As you can clearly see, the blockage from the JavaScript requests denies any other asset from downloading until the JavaScript file has completely downloaded. Even with this simple example, it took 3.43 seconds to render the page as compared to the prior example coming in at only 2.35 seconds a difference of 68.5%. The numerical results themselves should be reason enough to use LABjs.

Where Can I Get It

LAB.js can be found at the humble site, LABjs.com where you can download a zip file of the code with some examples. LABjs is maintained by Kyle himself and he is incredibly responsive to questions or requests so be sure to follow him on twitter.

Demystifying the DOM Ready Event Method

Probably the most fundamental, widely used, and most misunderstood method of the jQuery library is the ready() method (aka the “DOM Ready” method), commonly seen as $(document).ready() or simply $().ready() or even indirectly $(function(){...}). Why the third version is indirect? We’ll get to that later.

Many new web designers/developers that start to use jQuery usually write their first block of code starting with something like the following:

$(document).ready(function(){
    alert("Hello Whirled");
});

However, if you ask most people what is happening here, most likely the response is, “When the DOM is ready, I show an alert popup with the text ‘Hello Whirled’”.  However, there is no “DOM Ready” event in JavaScript; it is a rather ingenious method that will execute a block of JavaScript before the page has finished loading.  The benefits to having some JavaScript executed before the page has finished loading are immense, for one may need to manipulate the DOM prior to the page fully rendering, possibly some user agent detection, or whatever.  Waiting to execute that code using the traditional window.onload method doesn’t allow us to do these things.  But what is really happening when you call the ready() method?  I aim to sort that out from a technical perspective in this article.

Ready?

The ready() method does three things when it is called:

  1. Attaches listeners to the method by calling the bindReady() function.
  2. Checks to see if jQuery.isReady property is true and if it is, it executes the function immediately.
  3. Otherwise, it adds it to the stack (array) of functions to be executed when jQuery.isReady is in fact true.
jQuery.fn.extend({
    /* ... */
    ready: function(fn){
        // Attach the listeners
        bindReady();
        // If the DOM is already ready
        if (jQuery.isReady)
            // Execute the function immediately
            fn.call(document, jQuery);
        // Otherwise, remember the function for later
        else
            // Add the function to the wait list
            jQuery.readyList.push(fn);
        return this;
    }
});

The majority of the magic happens inside the bindReady() function:

var readyBound = false;
function bindReady(){
    if (readyBound)
        return;
    readyBound = true;
    // Mozilla, Opera and webkit nightlies currently support this event
    if (document.addEventListener) {
        // Use the handy event callback
        document.addEventListener("DOMContentLoaded", function(){
            document.removeEventListener("DOMContentLoaded", arguments.callee, false);
            jQuery.ready();
        }, false);
        // If IE event model is used
    }
    else
        if (document.attachEvent) {
            // ensure firing before onload,
            // maybe late but safe also for iframes
            document.attachEvent("onreadystatechange", function(){
                if (document.readyState === "complete") {
                    document.detachEvent("onreadystatechange", arguments.callee);
                    jQuery.ready();
                }
            });
            // If IE and not an iframe
            // continually check to see if the document is ready
            if (document.documentElement.doScroll && window == window.top)
                (function(){
                    if (jQuery.isReady)
                        return;
                    try {
                        // If IE is used, use the trick by Diego Perini
                        // http://javascript.nwbox.com/IEContentLoaded/
                        document.documentElement.doScroll("left");
                    }
                    catch (error) {
                        setTimeout(arguments.callee, 0);
                        return;
                    }
                    // and execute any waiting functions
                    jQuery.ready();
                })();
        }
    // A fallback to window.onload, that will always work
    jQuery.event.add(window, "load", jQuery.ready);
}

That’s a good bit of code, so let’s break it down into digestible chunks.

Standards-Compliant Browsers

The first, and most trivial check, is to see whether or not the readyBound variable is true and if it is, then we’re done! Otherwise, let’s set it to true now because when we only want/need to bind the handlers once.

if (readyBound)
    return;
readyBound = true;

Next, we see an example of browser feature detection being used for executing a particular block of code. Most standards-compliant browsers support the addEventListener() method. By passing the DOMContentLoaded event to the addEventListener() function we are telling the browser to “listen” for the event that is fired when a document’s DOM content has finished loading. The anonymous callback function that is passed as the second parameter to addEventListenter() is then executed when that event has fired. This anonymous event handler function removes itself (referenced via arguments.callee) from being bound to the document once it has been called. Finally, we call the jQuery utility method jQuery.ready(). We will discuss this method later on.

// Mozilla, Opera and webkit nightlies currently support this event
if (document.addEventListener) {
    // Use the handy event callback
    document.addEventListener("DOMContentLoaded", function(){
        document.removeEventListener("DOMContentLoaded", arguments.callee, false);
        jQuery.ready();
    }, false);
    // If IE event model is used
}

Internet Explorer is Special

In the next block of code, this bit of feature detection checks to see if Internet Explorer’s event model is used. To handle iframes, we need to “listen” for a “complete” response from the request sent by that iframe. document.attachEvent("onreadystatechange", function(){...}) handles this and fires the anonymous function when that response arrives. If it is in fact a “complete” response, then, similar to how we did before, the event is detached (no longer listening), document.detachEvent("onreadystatechange", arguments.callee) and the jQuery.ready() utility method is called.

 // Mozilla, Opera and webkit nightlies currently support this event
if (document.addEventListener) {
    // Use the handy event callback
    document.addEventListener("DOMContentLoaded", function(){
        document.removeEventListener("DOMContentLoaded", arguments.callee, false);
        jQuery.ready();
    }, false);
    // If IE event model is used
}

else
    if (document.attachEvent) {
        // ensure firing before onload,
        // maybe late but safe also for iframes
        document.attachEvent("onreadystatechange", function(){
            if (document.readyState === "complete") {
                document.detachEvent("onreadystatechange", arguments.callee);
                jQuery.ready();
            }
        });
        // If IE and not an iframe
        // continually check to see if the document is ready
        if (document.documentElement.doScroll && window == window.top)
            (function(){
                if (jQuery.isReady)
                    return;
                try {
                    // If IE is used, use the trick by Diego Perini
                    // http://javascript.nwbox.com/IEContentLoaded/
                    document.documentElement.doScroll("left");
                }
                catch (error) {
                    setTimeout(arguments.callee, 0);
                    return;
                }
                // and execute any waiting functions
                jQuery.ready();
            })();
    }

If we are not using iframes, then some true JavaScript trickery is used, so naturally we first want to verify that we are in fact using Internet Explorer and that it is not an iframe:

if (document.documentElement.doScroll && window == window.top)

Now, the good part. We have a self-invoking function that first, checks to see if the jQuery.ready property is true, for if it is then we return. Next, we use a trick (hack) by Diego Perini to see if the DOM Content is loaded by using the doScroll() method in a try/catch polling loop. This triggers the jQuery.ready() utility method when no errors are returned by the doScroll(). Pretty slick and a nice find by Diego. The catch(error) statement here is worth noting as the line containing the code setTimeout(arguments.callee, 0); essentially keeps calling this anonymous function until there are in fact NO errors, which then calls the jQuery.ready() utility method.

     (function(){
        if (jQuery.isReady)
            return;
        try {
            // If IE is used, use the trick by Diego Perini
            // http://javascript.nwbox.com/IEContentLoaded/
            document.documentElement.doScroll("left");
        }
        catch (error) {
            setTimeout(arguments.callee, 0);
            return;
        }
        // and execute any waiting functions
        jQuery.ready();
    })();

If all of this fails, then we simply attach a load event, to the window object, and THEN call the jQuery.ready utility method when this event has fired.

 // A fallback to window.onload, that will always work
jQuery.event.add(window, "load", jQuery.ready)

Where Am I?

Let’s recap where we’re at. By now, we have determined that either the DOM Content is loaded, regardless of the browser, OR we are falling back on the fail-safe window.onload() method. Now, we just need to execute the jQuery.ready() utility method.

jQuery.extend({
    isReady: false,
    readyList: [],
    // Handle when the DOM is ready
    ready: function(){
        // Make sure that the DOM is not already loaded
        if (!jQuery.isReady) {
            // Remember that the DOM is ready
            jQuery.isReady = true;
            // If there are functions bound, to execute
            if (jQuery.readyList) {
                // Execute all of them
                jQuery.each(jQuery.readyList, function(){
                    this.call(document, jQuery);
                });
                // Reset the list of functions
                jQuery.readyList = null;
            }
            // Trigger any bound ready events
            jQuery(document).triggerHandler("ready");
        }
    }
});

The jQuery.ready() utility method is concise and elegant in its design. It starts off by checking to see if the DOM is already loaded and if it is, then we skip to the last line of code in the ready method definition. If the DOM is not loaded, then we set it to be loaded if this method is called again some time in the future.  Then we check to see if the readyList property of the jQuery object (an array of functions) has a “truthyness” value of true, as it is null by default.  If it is true, then we pass this array of functions into the jQuery utility method, jQuery.each(), and iterate over each element of the array.  The second parameter of the jQuery.each() method is an anonymous function that is executed over each iteration.  In this case, we are calling each function in the readyList in the context of the document and passing the jQuery object parameter to each function so it may be used within that function call:  this.call(document, jQuery);

After we have iterated over the array of functions we then set the jQuery.readyList value to null, since we just executed all of those functions there is no need to keep track of them for the next potential jQuery.ready() utility method call.  Finally, we trigger any events that may have been bound to the ready event using the triggerHandler() method.

Indirect Call of Ready Method

Earlier I mentioned there was a third way of calling the jQuery.ready() method, namely $(function(){...}). By first glance, one may posit what exactly is going on here and how does it correlate to a ready() method call? First, we are creating a new jQuery object with the common $() notation. However, instead of passing in a selector such as $('#myDiv'), we are passing in an anonymous function. If you view the source code of jQuery 1.3.2, you will come across this block of code at line 80:

		// HANDLE: $(function)
		// Shortcut for document ready
		} else if ( jQuery.isFunction( selector ) )
			return jQuery( document ).ready( selector );

This code is found within the init() method of the jQuery object and is the final check in the init method to see if the value passed in, selector, is a function and if it is, then it returns a call to the ready() method passing in the anonymous function as the parameter. So the ready() method is still being called, just indirectly.

Thoughts?

Attempting to break the DOM Ready method down into bite-size pieces and still be comprehensible was no easy task. Please let me know if this article helps you out or if there is anything I can do to improve it. Thanks!

UPDATE: Hat tip to @getify for some quality feedback to improve this article.

A Simple JavaScript Performance Test With jQuery

One of the gifts in the swag bag from JSConf this year was a free copy of JSMag, a definite start to quality reading on topics related to JavaScript. I was reading a solid article by Rebecca Murphey (@rmurphey) on “Using Object Lilterals to Organize Your Features” and noticed something in the code that inspired me to do a quick little test on performance.

In her code, she has an object method defined in the following manner:

myFeature.$section_nav = $('ul/>').attr('id','section_nav').prependTo(myfeature.$container);

Absolutely nothing wrong with this code and it reads well and works fine. However, it is slightly different than the way I write my code when I’m dynamically creating and appending elements to the DOM. Typically, instead of creating an element and passing it to the jQuery method, I find the element I want to append the element to and then simply call the append method passing in a block of HTML. Again, nothing fancy, but the key difference I picked up on was that when I pass a block of HTML, I include all attributes in the element(s) I’m passing in. So essentially I would have:

$('#header').append("<p class="orange" id="headerText"/>");

So even though this may be just a coding style, I was curious to see what the difference would be in performance based on passing a block of HTML first, adding attributes to it via the attr() method and then appending (or prepending) to an element, or the way I do it, which is a bit of the reverse.

I created a very simple test with the following markup:

			<div id="main">
				<form action="">
					<input type="button" value="No Attribute Method Call" id="noAttr" />
					<input type="button" value="With Attribute Method Call" id="withAttr" />
					<input type="button" value="Clear Response Div" id="clear" />
				</form>
				<div id="response">

				</div>
			</div>

And the subsequent JavaScript:

$(function(){
    var $response = $('#response');

    $('#noAttr').click(function(){
        $response.append('<p id="myParagraph"/>');
        $('#myParagraph').text('Some Text');
        return false;
    });

    $('#withAttr').click(function(){
        $('<p/>').attr('id', 'myParagraph').appendTo($response);
        $('#myParagraph').text('Some Text');
        return false;
    });

    $('#clear').click(function(){
        var children = $response.children();
        $.each(children, function(i){
            delete children[i].parentNode.removeChild(children[i]);
        });
        return false;
    });

});

The results are interesting…

Test With Attribute Method

Test With Attribute Method

The above screenshot shows the results in Firebug’s profiler of the technique used that is similar to Rebecca’s example. Note, the total number of calls and the total amount of time it takes to execute the function on the click event.

Test Without Attribute Method

Test Without Attribute Method

And these are the results using the technique of which I am most accustomed.

The obvious numbers that stand out to me are the total number of calls and the total amount of execution time. 90 total function calls in the first example versus only 62 in the second and a difference of 0.811 milliseconds in total execution time. Now, many may argue that .811 milliseconds is not that significant of a difference on the surface, but the percentage difference is…a whopping 37.5%!

Try it out for yourself on the demo page (requires a standards-compliant browser and a JavaScript profiler).