Tutorials Books Videos Forums

Change the theme! Search!
Rambo ftw!

Customize Theme


Color

Background


Done

Table of Contents

Loading Script Files Dynamically

by kirupa   |   filed under JavaScript 101

In the Running Your Code at the Right Time time article, a part of what we looked at were the various ways we have to load and run external JavaScript files in our pages. All of these various ways assumed we knew exactly what script file we wanted to load with the src attribute already pointing to our file:

<script src="https://www.example.com/foo.js"></script>

Now, what if you were in the situation where you didn't know what script file you wanted to load at the time your page is loading? What if you had to choose between loading foo.js or bar.js depending on what actions the user took? Having a hard coded script element doesn't really work well in this case. What does work well is having a way to load our script file dynamically! In this short article, we will go into greater detail about it.

Onwards!

The Basic Technique

For loading a script file dynamically using JavaScript, the basic steps are:

  1. Create the script element
  2. Set the src attribute on the script element to point to the file we want to load
  3. Add the script element to the DOM

This numbered list turned into code looks like the following three lines:

let myScript = document.createElement("script");
myScript.setAttribute("src", "https://www.example.com/foo.js");
document.body.appendChild(myScript);

The myScript variable stores a reference to our newly created script element. The setAttribute method allows us to set the src value for the script we'd like to load. We seal the deal by adding our script element to the bottom of our body element via appendChild. If some of these steps seem a bit outlandish, the Creating, Removing, and Cloning DOM Elements tutorial will get you familiarized with the fun world of DOM manipulation.

To see this code in action as part of a fully working example, create a new HTML document and add/copy/paste the following content into it:

<!DOCTYPE html>
<html>

<head>
  <title>Dynamic Script Loading</title>
  <style>
    body {
      padding: 50px;
      background-color: #EAC5D8;
    }

    h1 {
      font-family: sans-serif;
      font-size: 128px;
      margin: 0;
      line-height: 1em;
      font-weight: bold;
      color: #D68FB5;
    }
  </style>
</head>

<body>
  <h1>I am <br>in your<br> code!</h1>

  <script>
    let myCoolCode = document.createElement("script");
    myCoolCode.setAttribute("src", "https://www.kirupa.com/js/easing.js");
    document.body.appendChild(myCoolCode);
  </script>

</body>

</html>

Take a moment to look at all that is contained here. We have some HTML and CSS,...and by now that isn't anything exciting to write home about. Mainly, we have a script element that contains some code to dynamically load a file called easing.js and append it to the bottom of our body element. To see all of this in action, save your HTML document and preview it in your favorite browser.

What you will see in your favorite browser will look something like the following:

What we visually see doesn't really tell us much. What we need to do is go undercover and inspect the live version of our DOM! For that, we need to bring up the browser developer tools and Inspect the page to see exactly what the browser sees. When we do this, notice that our dynamically created script element shows up in the DOM:

To go even further, we can inspect the Network traffic and see that the easing.js script file referenced by our script element gets loaded as well:

If easing.js isn't showing up for you, refresh the page with the Network inspector tab open. That will ensure you can see the external script file being requested and then loaded.

At this point, we have looked at the basics of how to load an external script file using just a few lines of JavaScript. This doesn't mean we have to be done, though. There are some quirks and edge cases that might bite us if we aren't careful, so the next couple of sections will prepare us to not get bitten...or bite back?!! 😇

Running Our Dynamically Loaded Script First

Adding a script element to the bottom of the body element means that our page will render first without being blocked on our JavaScript from loading and executing. That is usually the correct behavior we want. Now, there will be cases when you want the JavaScript to run first ahead of anything else your page might do. To handle those cases, we need to adjust our code.

Take a look at what we are now doing:

let myScript = document.createElement("script");
myScript.setAttribute("src", "https://www.example.com/foo.js");
myScript.setAttribute("async", "false");

let head = document.head;
head.insertBefore(myScript, head.firstElementChild);

There are two new things going on in the code that ensure our external script file is loaded and run before anything else on the page is rendered:

  1. We first set the async attribute on our script element to false. Why do we do that? It is because dynamically loaded script files are loaded asynchronously by default. We want to explicitly override that default behavior.
  2. Next, we ensure we load our script before the rest of the page loads. Adding our script element at the top of the head element is the best place to ensure it runs ahead of anything else the page might be up to.

If we modify our full example to load our external script file first, here is what the full HTML, CSS, and JS will look like:

<!DOCTYPE html>
<html>

<head>
  <title>Dynamic Script Loading</title>
  <style>
    body {
      padding: 50px;
      background-color: #EAC5D8;
    }

    h1 {
      font-family: sans-serif;
      font-size: 128px;
      margin: 0;
      line-height: 1em;
      font-weight: bold;
      color: #D68FB5;
    }
  </style>
  
  <script>
    let myCoolCode = document.createElement("script");
    myCoolCode.setAttribute("src", "https://www.kirupa.com/js/easing.js");
    myCoolCode.setAttribute("async", "false");

    let head = document.head;
    head.insertBefore(myCoolCode, head.firstElementChild);
  </script>
</head>

<body>
  <h1>I am <br>in your<br> code!</h1>
</body>

</html>

There is one additional change the larger example calls out that is relevant here. The code for actually loading our external script file needs to be inside the head element as well. If we kept this code at the bottom of the page like we saw earlier, our page will still render and load everything as usual before even realizing it needs to handle loading an external script file. At that point, it doesn't matter if our external script file is loaded from the top of the page or the bottom of the page. The page's DOM has already loaded 🤦‍♂️

Running Dependent Code After Our Script File Has Loaded

We just have one last tidbit around dealing with dynamic script files that we will look at before wrapping things up. It is common to load an external script file and then call a function (or rely on something from the loaded script) immediately afterwards. Below is one such example of what that this traditionally looks like:

The first script element loads docsearch.min.js. The second script element calls something dependent on docsearch.min.js loaded by the earlier script element. All of this just works because the browser handles this scenario naturally. Best of all, we get this behavior for free.

For dynamically loaded script files, if we want to ensure similar behavior, we have a small amount of extra work to do. This extra work involves listening to our script element's load event and, once this event is overheard, calling any dependent code afterwards. This will make more sense when we look at the code:

let myScript = document.createElement("script");
myScript.setAttribute("src", "https://www.example.com/foo.js");
document.body.appendChild(myScript);

myScript.addEventListener("load", scriptLoaded, false);

function scriptLoaded() {
  console.log("Script is ready to rock and roll!");
}

Let us take a moment to walk through what is going on:

  1. Our trusty old myScript element is loading foo.js
  2. Once foo.js fully loads and executes, myScript will fire the load event
  3. The addEventListener call that is listening for the load event will overhear it and, in turn, call the scriptLoaded event handler.
  4. Any code that lives inside scriptLoaded can call and access any method or property that comes from foo.js and its contents

If you need a fun refresher on how to work with events and event handlers, check out the Events in JavaScript article.

Conclusion

As you and I build web sites and apps that get increasingly more dynamic, we wouldn't want to overwhelm our users by loading every possible script file that they may need up front. The techniques we looked at in this tutorial highlight one way to break up when our script files load by, essentially, loading them on-demand. So...yeah!

Just a final word before we wrap up. If you have a question and/or want to be part of a friendly, collaborative community of over 220k other developers like yourself, post on the forums for a quick response!

Kirupa's signature!

The KIRUPA Newsletter

Thought provoking content that lives at the intersection of design 🎨, development 🤖, and business 💰 - delivered weekly to over a bazillion subscribers!

SUBSCRIBE NOW

Creating engaging and entertaining content for designers and developers since 1998.

Follow:

Popular

Loose Ends

:: Copyright KIRUPA 2024 //--