Queue events that occur before JavaScript is loaded

One of the common recommendations for speeding up your website is to put your JavaScript at the bottom of your page instead of including it inside the head tag. The difference this simple placement can have is impressive, especially if you are dealing with sizable JavaScript libraries that are usually 50k at best.

One downside with putting your JavaScript at the bottom is that your fast clicking visitors may click on links that won’t work. The reason this happens is because the JavaScript that those links trigger hasn’t been downloaded yet. Usually those links will work on the second or third try, but it makes for a bad user experience and a poor first impression.

I decided to fix it by queuing up those user-triggered actions and replaying them as soon as the document is ready. I wrote a wrapper that I can use anytime I have code that depends on my JavaScript being downloaded and available.

The concept is simple. Instead of calling functions directly when a user triggers an action, I add the function call to a queue. When the document is ready, I loop through the queue and execute each of the actions in the order that they occurred. I put this code inline inside my head tag so it is available as soon possible. The rest of my JavaScript can then be included right before the closing body tag without worrying about this race condition between the browser and website visitor.

<script type="text/javascript">

var loaded = false;
var action_queue = new Array();

function when_ready(callback) {
    // skip the queue if the document has already loaded
    if (loaded == true)
        eval(callback);
    else {
        action_queue.push(callback);
    }
}

function dequeue_actions() {
    for (i in action_queue) {
        eval(action_queue[i]);
        delete(action_queue[i]); // cleanup after ourselves
    }
    loaded = true;
}

</script>

I then trigger dequeue_actions() as soon as the document is ready:

// using jQuery
$(document).ready(function(){
    dequeue_actions();
});

// this works too
onload = dequeue_actions;

You can then safely make function calls using when_ready(). For example:

<a onclick="select('foo')">foo</a>

becomes

<a onclick="when_ready('select(\'foo\')')">foo</a>

In my testing, the results have been very smooth with delays being almost unnoticeable. Of course, your experience will vary depending on the size of your document and how long it takes for your document to be ready.

This code is pure JavaScript and should work in every modern browser. I’ve tested it in IE6+, FF2+ and Safari 3+.