JavaScriptHow to

Essential JavaScript: the top five script loaders

Essential JavaScript: the top five script loaders

Jack Franklin explains how JavaScript script loaders work and examines five of the most popular ones to help you decide which one to use for your project.

This is the first in a series of regular articles that will examine resources in a number of categories, including front-end frameworks like Backbone.js, templating engines like Handlebars.js and a number of others. The number of JavaScript resources is growing every day, which is great for developers, but often it can become confusing or overwhelming to pick which one best suits the situation in hand. Often there's no definitive solution and hopefully this series will provide information and cases where particular solutions are best.

How script loaders work in general

The theory is that script loaders, when loading in multiple scripts through the use of <script> tags, block the page loading; a page can only render itself once all the JS scripts have been loaded. This can be partially negated by minifying all your scripts into one file but even so, it will still block the page rendering. Hence, script loaders that can load in scripts both asynchronously and in parallel (multiple files concurrently) offer a distinct advantage. Even if you're combining and minifying your JS into one file, including it through a script loader will often perform better than including it traditionally through a <script> tag.

Most script loaders follow a very similar pattern in their usage. The first step is to pass in file names to be loaded:

genericScriptLoader.load("jquery.js", "underscore.js");

Because the scripts are being loaded asynchronously, you cannot simply run any code that relies on those scripts when you like. Usually we include code than depends on our scripts below the <script> tags that load the libraries required; when loading asynchronously this won't work, as you cannot guarantee your dependencies will be loaded when your code is executed. The solution all script loaders provide is a method that will be invoked once the scripts have loaded. These accept a function which will execute when all scripts are loaded.

    genericScriptLoader.ready(function() {
        //your code goes here
     });

Now we've covered the basics of a generic script loader, let's take a look at five I've picked out and the pros and cons of each.

Advertisement

01. HeadJS

The first is HeadJS, created by Tero Piirainen. HeadJS was one of the most popular script loaders but has not been updated in recent months, with the last commit on the Github repository being ten months ago. Despite this, it remains an excellent script loader that is worth examining. Basic usage of HeadJS is very straight forward:

    head.js("jquery.js", "app.js", "twitter.js", function() {
        //called when all scripts have been loaded
    });

If you'd rather not pass the code in as a callback to the loading function, you can use head.js() to load in the files and then pass in your code to head.ready:

     head.js("jquery.js", "app.js");
      head.ready(function() {
        //called when all scripts have been loaded
      });

One of the best features of HeadJS is the ability to label your scripts, and run code once certain ones have loaded. For example, let's say you're loading in two scripts, jQuery and Google Analytics, but your code only relies on jQuery, so you want to execute it as soon as jQuery is loaded. HeadJS offers a really nice way of doing this:

       head.js({ jQuery: "path/to/jquery.js" },
              { gAnalytics: "path/to/analytics.js" });
     
      head.ready("jQuery", function() {
        //code that relies on jQuery but not analytics.js
      });

If you don't want to label the files, you can pass in head.ready() the file name and it achieves the same thing:

      head.js("path/to/jquery.js", "path/to/analytics.js");
     
      head.ready("jquery.js", function() {});

02. RequireJS

Next up, it's time to take a look at RequireJS. RequireJS works slightly differently to HeadJS and other loaders. The first thing you have to do is include the RequireJS source, but also set the data-main attribute on the <script> tag which tells RequireJS the path to your main JavaScript file. For example:

   <script data-main="app" src="require.js"></script>

That presumes your main JS file is titled "app.js" and is in the same directory as the file that <script> tag is in. If your JS was in a sub directory of "<scripts>", you would give RequireJS this path:

<script data-main="scripts/app" src="require.js"></script>

Note how you don't have to add ".js" onto the end of that, RequireJS has that covered for you.

Now, within the main file (in my case, app.js) we can load in scripts by using the require() function. The require() function takes two arguments, the scripts to load and the function to execute once those scripts have loaded. Keep in mind that the script paths are relative to the path of app.js:

           require(['lib/jquery'], function() {
        //code to execute when jQuery is loaded
      });

That's basic usage of RequireJS, but it's actually capable of much more. RequireJS implements the AMD (asynchronous module definition) spec, which is a method for defining modules which can be loaded. You write all your code as modules, mainly through RequireJS's define() function. This is a great approach when you have a large application with multiple scripts, all depending on certain others. Whilst this is far too much to go into in this article (I could write multiple articles purely on RequireJS), Jim Hoskins has written a great post on creating and using your own modules with RequireJS that I highly recommend.

03. yepnope.js

yepnope.js is a script loader with a slightly different objective than HeadJS or RequireJS. yepnope is designed to be a resource loader that can load in scripts conditionally, based on any statement you can evaluate to either true or false. It's designed for loading in polyfills. How often have you had to write some extra JS because the browser doesn't support something? It's something that happens pretty often, especially if you're using brand new APIs and features that not all browsers have implemented yet. It fits perfectly with a feature detection library like Modernizr - so much so that the Modernizr source actually contains yepnope within it.

Using yepnope is easy. We use the yepnope() function, passing in an object which looks something like this:

     yepnope({
        test: Modernizr.placeholder,
        nope: "placeholder-polyfill.js"
      });

Here I load in a polyfill for HTML5 placeholder attributes if it's not natively supported. Of course, we are not limited to just Modernizr tests. What if we are adding a class to the <html> tag of ".ie" if the browser is IE8 or less, and want to load in files based on that?

       yepnope({
        test: $("html").hasClass("ie"),
        yep: "ie-fixes.js",
        nope: "non-ie.js"
      });

It's also easy to load a script with no conditional through "load" and also perform a test that relies on that script:

      yepnope({
        load: ['path/to/jquery.js', 'app.js'],
        complete: function() {
          $(function() {
            yepnope({
              test: $("html").hasClass("ie"),
              yep: "ie-fixes.js",
              nope: "non-ie.js",
            });
          });
        });
      });

It's worth noting that yepnope is not necessarily the quickest script loader, that's not its aim, but it does offer conditional loading that can't be matched by any other resource out there.

04. LABjs

Our penultimate offering in this article is LABjs, written by Kyle Simpson. It is designed to be an all purpose JavaScript loader that loads scripts efficiently and quickly. Its syntax differs slightly from others covered in this article, but it's still easy to understand and use.

For example, let's say we have jQuery and a plugin to load. With LABjs we'd do it like so:

  $LAB.script("path/to/jquery.js").wait()
      .script("path/to/plugin.js")

Rather than pass in an array of files, we call .script() for every file we want to load in. LABjs also signifies dependencies differently. Here .wait() means to wait for jQuery to load, before loading in the plugin. This means you can be confident jQuery is loaded before the plugin is. If you want to run some code once everything is loaded, pass a function into wait():

  $LAB.script("path/to/jquery.js").wait()
      .script("path/to/plugin.js")
      .wait(function() {
        //code to run once both files are loaded
      });

You can read more on LABjs usage in the documentation. However it's important to note that as I was writing this article, it was announced that there will be no more development on LABjs. Whilst this is a shame, it remains a reliable script loader (and the code is on Github too if you want to maintain it or fix bugs) and I felt it worthwhile of including in this article.

05. LazyLoad

Last, but by no means least, I want to take a look at LazyLoad, by Ryan Grove. LazyLoad sets out to do one thing and one thing well. It's the smallest of all loaders I've shown you (less than 1KB minified) and is deliberately written to be small and simple to use, for loading in CSS and JavaScript. It's functionality set is smaller than some other solutions but that's to be expected. It's easy to use for loading in one or multiple files:

  LazyLoad.js("jquery.js", function() {
    //jQuery loaded
  });

  LazyLoad.js(["jquery.js", "plugin.js"], function() {
    //multiple files all loaded
  });

LazyLoad loads files in parallel, not in order, so when you need to load a script but only once another one has, the best approach is:

  LazyLoad.js("jquery.js", function() {
    LazyLoad.js("plugin.js", function() {
      //execute plugin
    });
  });

The functionality is very similar for loading in CSS, but it's done through the .css() function.

06. Conclusion

I hope this article has served to demonstrate some popular libraries and how best to use them. Different libraries do different things better and you should always look to pick the best for the situation in hand. Personally, anything more than a couple of simple scripts, I tend to look towards RequireJS, which is packed full of functionality. If I'm working with polyfills, it's yepnope every time and for something simple I usually go for HeadJS. However, that's not to say those choices would suit you, so I encourage you to try as many as you can to get a good knowledge of them and be able to pick out the best one for the situation in hand.

Jack Franklin is a 20 year old developer living in London, UK. He runs a JavaScript blog at javascriptplayground.com and also writes PHP, Ruby and other languages. He tweets as @Jack_Franklin.

Liked this? Read these!

Advert

Log in to Creative Bloq with your preferred social network to comment

OR

Log in with your Creative Bloq account

site stat collection