JavaScript

Loading jQuery asynchronously

The recommended way to load jQuery is with a script tag. You place it on the bottom of your page and make sure every script that depends on it are loaded after. However, what happens when you want to load all your scripts Asynchronously. No script can depend on another.

If your own script loads before jQuery it won't run. If you are using the document ready event, it will simply throw an error.

Uncaught ReferenceError: $ is not defined(…)

The problem is, there is no guarantee on the order in which the browser will download the scripts. We have to make sure our code that depends on jQuery is only ran once the library is present.

In a previous post, I talked before about how we can call functions that are not defined and still get away with it. The trick is to use asynchronous functions. We will use an array to store our functions to call, and add a snippet of code at the end of our jQuery file to do the trick.

var async = async || [];
async.push(["ready",function (){
    // the document is ready
    $("#overlay").show("fast");

    // more jQuery dependent code
    ...
}]);

This code can run anytime, even if jQuery is still not present on the page. All it does is store your function in an array with with meta data (ready), that can be accessed later.

jQuery can be loaded on top or the bottom of the page, it doesn't matter really.

<script>
(function () {
    var script = document.createElement(s); 
    script.type = 'text/javascript'; 
    script.async = true;
    script.src = 'jquery.js';
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(script, s);
})();
</script>

Method 1: Modify the jQuery file

In order to run our code, we have to make sure jQuery checks our async variable for the list of functions to run. We can add this snippet of code at the end of jquery.js

// Jquery code 
....
// end of code 
var async = async || [];
while(async.length) { // there is some syncing to be done
    var obj = async.shift();
    if (obj[0] =="ready") {
        $(obj[1]);
    }else if (obj[0] =="load"){
        $(window).load(obj[1]);
    }
}

The code is self explanatory. The async variable is looped through and depending on what the first parameter is, the functions are loaded when the document is loaded or ready.

But this will only run for the functions defined before jquery is loaded. What about the rest? Well, we can modify the async variable to be an object with a method .push() to continue receiving functions.

So right after the previous script, we can add this:

async = {
    push: function(param){
        if (param[0] =="ready") {
            $(param[1]);
        }else if (param[0] =="load"){
            $(window).load(param[1]);
        }
    }
};

Now every time a user calls the async.push() method, it will call the function on our new object instead of storing it in an array.

Method 2: Detect when jQuery is loaded

It is very common for people to load jQuery from a CDN. The problem this brings is that we cannot modify the script. Instead we can detect when jQuery is loaded and run our code. We can add our own script that we control to do so.

// loaded anywhere on the page asynchronously.
(function jqIsReady() {
    if (typeof $ === "undefined"){
        setTimeout(jqIsReady,10);
        return ;
    }
    var async = async || [];
    while(async.length) { // there is some syncing to be done
        var obj = async.shift();
        if (obj[0] =="ready") {
            $(obj[1]);
        }else if (obj[0] =="load"){
            $(window).load(obj[1]);
        }
    }

    async = {
        push: function(param){
            if (param[0] =="ready") {
                $(param[1]);
            }else if (param[0] =="load"){
                $(window).load(param[1]);
            }
        }
    };
})();

The script checks if jQuery is loaded. If not, it waits 10 milliseconds and checks again. Only when it is present will it run the functions and replace the async variable.

Method 3: On script tag load

updated 2016-04-18

Somehow I did not think of this method earlier, but it is possible to detect when a script tag content is loaded. We just have to make sure it is cross browser compatible. We can use the same async variable to store functions and run them only when jQuery is ready.

<script>
(function () {
    var done = false;
    var script = document.createElement("script"),
    head = document.getElementsByTagName("head")[0] || document.documentElement;
    script.src = 'jquery.js';
    script.type = 'text/javascript'; 
    script.async = true;
    script.onload = script.onreadystatechange = function() {
        if (!done && (!this.readyState || this.readyState === "loaded" || this.readyState === "complete")) {
            done = true;
            // Process async variable
            var async = async || [];
            while(async.length) { // there is some syncing to be done
                var obj = async.shift();
                if (obj[0] =="ready") {
                    $(obj[1]);
                }else if (obj[0] =="load"){
                    $(window).load(obj[1]);
                }
            }
            async = {
                push: function(param){
                    if (param[0] =="ready") {
                        $(param[1]);
                    }else if (param[0] =="load"){
                        $(window).load(param[1]);
                    }
                }
            };
            // End of processing
            script.onload = script.onreadystatechange = null;
            if (head && script.parentNode) {
                head.removeChild(script);
            }
        }
    };
head.insertBefore(script, head.firstChild);
})();
</script>

With this method guaranties that jQuery is on the page before any of the code is executed.


Now you can write code that depends on jQuery without worrying if the library is loaded on the page yet. The advantage is that you can load all your scripts asynchronously and none of them will block the rendering of the page.


Comments

Henry Reith :

Thank you so much for this. I have just run a Google Pagespeed test on my site and its pretty much perfect apart from on Mobile I score red for blocking scripts, that Jquery is my main script.

So I am going to have to try this and see if i can get it working with Wordpress.

Ibrahim :

Hi Henry,

I used to have jQuery running on this website too and Google Pagespeed marked it as red. Now all my scripts are async. One thing that I was having a hard time with was the CSS, because those too block the page. I have my CSS loading inline, which is tricky, but if you want to know how here is a how I did it.

Huyen Vu :

I followed yours tips but have an error:

"Uncaught ReferenceError: async is not defined" at line: "async = async || [];".

Please help me. Thanks.

Ibrahim :

Hi Huyen Vu. Thank you for finding this error.

I updated the code to work now. What was missing was the keyword var. So here is how it is supposed to work:

var async = async || [];
while(async.length) { // there is some syncing to be done
    var obj = async.shift();
    if (obj[0] =="ready") {
        $(obj[1]);
    }else if (obj[0] =="load"){
        $(window).load(obj[1]);
    }
}

Cheers!

Michael :

Ok, new to this myself so apologies if this is a stupid question :) But when using the method two, I suppose I have to run that function jqIsReady somewhere?

I mean, do I have to launch it: jqIsReady(); or edit my jquery somehow?

Or do I just pretty much copy paste it as it is, load jquery async and it all works?

Ibrahim :

Hi Michael,

All you have to do is copy the script. If you take a look at the function jqIsReady(), it is a self invoking function. I only gave it a name so we can call it over and over unlti jquery is loaded. Check line 4 and you will see this:

setTimeout(jqIsReady,10);

So to test it, copy and paste the code, and add a console.log('test') after the condition to see when jquery is loaded.

I hope this makes sense :)

Michael :

Ok, thank you! I'll try that out. Made sense indeed :)

teo :

Hi, I wonder how could I use your jqIsReady function to load jQuery code only after jQuery is loaded, something like:

jqIsReady(function(){
   //jQuery code to be executed after jQuery is loaded
});

(I get Uncaught ReferenceError: jqIsReady is not defined)

Thank you!

Ibrahim :

Hi teo

if you use the code I wrote on this post, you can call functions when jquery is load. However, not with the function jqIsReady.

Here is how you can do it instead:

var async = async || [];
async.push(["ready",function (){
   //jQuery code to be executed after jQuery is loaded
}]);

But of course, you can always modify my code to make it work your way. In that case you would have to define the function jqIsReady() on top of the page, and make sure you add all functions called in an array if jquery is not present, and use the setTimeout option or onload function to trigger all functions. Soon I will update this post with a method 3 for checking when jquery is loaded.

Don't forget to check back in a few days.

PS: use 4 spaces before your code for markdown :)

:) I am code

Moni :

Hello, i try this and it work form me on Chrome(OSX). I use Method 2, but its not work on Mozilla(OSX and Win) and IE. Any suggestions?

Regards! :)

Ibrahim :

Hi Moni,

Can you tell me what didn't work? Was there any error? It always help to add console.log to your code to make sure it is running.

Mahesh Vedurupaka :

Hi, none of the methods are working for me. i tried all, but still getting $ is not defined and jquery is not defined errors in console log. please tell me where i am missing.

Ibrahim :

One of the important questions is "How are you calling jQuery?"

Try to make use of the third method (don't forget to update the link to jQuery), then call jQuery this way:

var async = async || [];
async.push(["ready",function (){
   alert("Hello jQuery");
   // note that $ is available here.
}]);

Let's hear your thoughts

For my eyes only