Dynamically Loading A JavaScript File

Overview

Today, we will have a look at different ways to dynamically load JavaScript files after the page is fully loaded.

Introduction

Dynamic loading of JavaScript files is one of the very useful tools that you must have in your set. It allows you to optimize web page performance by moving blocking scripts off the loading process (usually they call this “lazy loading”), and to load scripts only when the user needs them (usually they call this “on-demand loading”.) This tool will greatly improve your page performance if you use it wisely.

Setting Up

Let us define our example model, we will start by defining the remote file that needs to be dynamically loaded. Here’s the definition for the “remote.js” file.

// this is going to be executed when script is loaded
(function() {
    console.log("Remote script loaded");
}());
var sayHello = function(name) {
    alert("Hello", name);
}

In the above code, we define an immediate function to track file loading. We also define a custom function to be called from our main page.

Now, here is our main “index.htm” page. It contains only a single button that will be used to load and test the file.

<html>
  <head></head>
  <body>
    <button id="loadButton">Load script file</button>
    <script type="text /javascript">
      document.getElementById('loadButton').onclick = function() {
      // your code goes here
      };
    </script>
  </body>
</html>

The No-Brainer Way!

The most straightforward way to load a JavaScript file is to reference it in an <script> element. The easiest way to dynamically load this file is to dynamically load this element! (You didn’t see that coming? Did you?)

Let us update the code and see results in action,

document.getElementById("loadButton").onclick = function() {
    var script = document.createElement("script");
    script.src = "remote.js";
    script.onload = function() {
        sayHello("Mohammad");
    };
    // append and execute script
    document.documentElement.firstChild.appendChild(script);
};

The above code simply creates an <script> element and sets the src field of this element to the path of our file. Then it uses the appendChild() function to append it to the first child of our code, the <head> element. The following code results in the following,

Dynamically Loading a JavaScript File

Now, let us refactor the above code and modify it a little bit to be able to use it anywhere else,

/**
 * Used to load and execute javascript file. an be used cross-domain seamlessly.
 * @param file JS file name
 * @param callback Subscribe to get notified when script file is loaded
 **/
function require(file, callback) {
    // create script element
    var script = document.createElement("script");
    script.src = file;
    // monitor script loading
    // IE < 7, does not support onload
    if (callback) {
        script.onreadystatechange = function() {
            if (script.readyState === "loaded" || script.readyState === "complete") {
                // no need to be notified again
                script.onreadystatechange = null;
                // notify user
                callback();
            }
        };
        // other browsers
        script.onload = function() {
            callback();
        };
    }
    // append and execute script
    document.documentElement.firstChild.appendChild(script);
}
document.getElementById("loadButton").onclick = function() {
    require("remote.js", function() {
        sayHello("Mohammad");
    });
};

Now you can easily call require() along with a JavaScript path and a callback function to be notified when the script is loaded.

The Backbreaking Way!

Another way to dynamically load a JavaScript file is to retrieve it using a classic HTTP request. This is a pure JavaScript call, and yet it has many drawbacks.

Let us see it in action,

/**
 * Used to load and execute javascript file. Suffers from same-domain restriction.
 * @param file JS file name
 * @param callback Subscribe to get notified when script file is loaded
 **/
function requireXhr(file, callback) {
    // object initialization
    const xhr = new XMLHttpRequest();
    // subscribe to request events
    xhr.onreadystatechange = function() {
        // readyState:
        // 0 UNSENT Client has been created. open() not called yet.
        // 1 OPENED open() has been called.
        // 2 HEADERS_RECEIVED send() has been called, and headers and status are available.
        // 3 LOADING Downloading; responseText holds partial data.
        // 4 DONE The operation is complete.
        // when not done, return
        if (xhr.readyState !== 4) {
            return;
        }
        // done, check status code
        if (xhr.status !== 200) // 200 = OK
        {
            return;
        }
        // now the file is loaded,
        // go and execute the script
        eval(xhr.responseText);
        // notify caller
        if (callback) {
            callback();
        }
    };
    // open connection to file
    xhr.open("GET", file, true);
    // send the request
    xhr.send();
}
document.getElementById("loadButton").onclick = function() {
    requireXhr("remote.js", function() {
        sayHello("Mohammad");
    });
};

The code is very simple and self-descriptive. We set-up an HTML request using XMLHttpRequest object, and we fire it. Then, we monitor its status changes and perform accordingly.

When we execute the above code, we get the following results. We can see our successful XHR request in the DevTools extension,

On the other hand, the results were disappointing. We were able to run the script successfully and there is no clue of the script file in the HTML content, however, due to the private scope of eval(), we are not able to call the loaded functions.

Another drawback of this way is that it suffers a lot when working cross-domain. And you cannot load from a local path too!

A Piece of Cake!

Now comes the most straightforward way to lazy-load a JavaScript file, the jQuery way. jQuery provides a function called getScript() (which is a shorthand of ajax()) to retrieve and load JavaScript files.

Here is the code,

/**
 * Used to load and execute javascript file. 
 * @param file JS file name
 * @param callback Subscribe to get notified when script file is loaded
 **/
function requireAjax(file, callback) {
    jQuery.getScript(file, callback);
}
document.getElementById("loadButton").onclick = function() {
    requireAjax("remote.js", function() {
        sayHello("Mohammad");
    });
};

Although jQuery.getScript() uses XMLHttpRequest internally, and you can see the request in XHR list in DevTools, it does not suffer from the same drawbacks (like same-domain restriction) as XMLHttpRequest.

The results are great too,

Dynamically Loading a JavaScript File

Conclusion

To conclude, if you have jQuery enabled on your page, you can easily use jQuery.getScript() to load a JavaScript asynchronously on-demand. If you are looking for a pure JavaScript approach, go for the <script> method, which has little overhead incurred.

Source Code

The example is available on GitHub on the following location:

https://github.com/elsheimy/Samples.DynamicLoadJavaScriptFile

Thank you!

Thanks for reading. I hope you have enjoyed reading this! 😊