Module loading in Javascript is still finnicky in 2015; here's a guide to making your life a little easier with the popular RequireJS library.

The creation and loading of Javascript modules  is still a headache-inducing chore in 2015. As time goes on, the non-evergreen 1 browsers of yore are becoming less common, meaning we’ll have to wait less for ES6 compatible browsers with native JS module support. But developing for the web is definitely a “here and now” situation, meaning until ES6 support is widespread, we still have to use hacky workarounds to get any form of module loading and dependency management in our browsers.

The most common module loader out there, RequireJS, is complicated to set up, to say the least. For a newbie to modular code like myself, who has no understanding of concepts like dependency injection 2, it’s incredibly daunting to look at the docs that RequireJS provides. I thought I’d keep it simple and show you how you can make use of  RequireJS’ modular loading, with a real world example of how I’ve used RequireJS on my website. I make use of three key Javascript libraries and my own custom code on my website, each with it’s own set of dependencies:

  • JQuery
  • ZURB Foundation - dependent on JQuery and Modernizr
  • TypedJS - dependent on JQuery
  • My configuration code - all of the above

Specifying, managing and updating these dependencies could be a difficult process, and whilst I could just pop them all in

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

where data-main is the location of your main Javascript file, responsible for specifying all other Javascript. Format the top of your main.js file like so:

// RequireJS Configuration
requirejs.config({
  baseUrl: 'js',
  paths: {
    jquery: '../bower_components/jquery/dist/jquery',
    foundation: '../bower_components/foundation/js/foundation',
    modernizr: '../bower_components/modernizr/modernizr',
    typed: '../bower_components/typed.js/js/typed'
  },
  shim: {
    foundation: {
      deps: ['jquery', 'modernizr']
    },
    typed: {
      deps: ['jquery']
    }
  }
});

As you can see, all libraries and their paths are specified. Now, some of these libraries will have their dependencies specified in their own source code, as they follow the UMD - Modernizr is an example of this. An easy way to tell is to look in the source code for the ‘define’ keyword - if it has it, it probably implements UMD, and you just need to add each of the defined modules it depends on to your ‘paths’ object. For those that don’t, you will need to use the ‘shim’ definition, as seen above. You just need to specify the named libraries that your shimmed library is dependent on, and RequireJS will handle the rest. Now for the fun part; specifying the dependencies of your main.js code.

require(['jquery', 'foundation', 'typed'], function($, foundation, typed) {
  // Foundation JavaScript
  // Documentation can be found at: http://foundation.zurb.com/docs
  $(document).foundation();
  //Typed definitions and initialisation
  var allStringsToChooseFrom = [];
  var selectedStrings = ["write neat code.", "study info technology", "love music.", "sometimes design websites."];
  $("#typedJsI").typed({
    strings: selectedStrings,
    typeSpeed: 30,
    backSpeed: 30
  });
  $(document).ready(function(){
    $('a[href^="#"]').on('click',function (e) {
      e.preventDefault();
      var target = this.hash;
      var $target = $(target);
      $('html, body').stop().animate({
        'scrollTop': $target.offset().top
      }, 900, 'swing', function () {
        window.location.hash = target;
      });
    });
  });
});

Notice how require() takes two arguments; an array of strings that represent the ACTUAL objects to inject, and then a function which has arguments corresponding to those objects. These arguments are what we use to refer to the objects within our require’d code. A clear example of this is how ‘jquery’ maps to the symbol $, and is referred to throughout my code snippet above. This example shows the true power of RequireJS:

  • you don’t end up polluting the global namespace, and it handles all of the dependency resolution for you - the required libraries can be asked for in any order.</li>
  • you can use multiple versions of the same library with different ‘path’ values, and spread updates to your libraries on a module-by-module basis to ensure you don’t make breaking changes all over
  • the required libraries can be asked for in any order, and Require will wire it all up for you (just keep track of your own modules and enforce separation of concerns so you don’t end up with a circular dependency.3

The final main.js:

// RequireJS Configuration
requirejs.config({
  baseUrl: 'js',
  paths: {
    jquery: '../bower_components/jquery/dist/jquery',
    foundation: '../bower_components/foundation/js/foundation',
    modernizr: '../bower_components/modernizr/modernizr',
    typed: '../bower_components/typed.js/js/typed'
  },
  shim: {
    foundation: {
      deps: ['jquery', 'modernizr']
    },
    typed: {
      deps: ['jquery']
    }
  }
});
require(['jquery', 'foundation', 'typed'], function($, foundation, typed) {
  // Foundation JavaScript
  // Documentation can be found at: http://foundation.zurb.com/docs
  $(document).foundation();
  //Typed definitions and initialisation
  var allStringsToChooseFrom = [];
  var selectedStrings = ["write neat code.", "study info technology", "love music.", "sometimes design websites."];
  $("#typedJsI").typed({
    strings: selectedStrings,
    typeSpeed: 30,
    backSpeed: 30
  });
  $(document).ready(function(){
    $('a[href^="#"]').on('click',function (e) {
      e.preventDefault();
      var target = this.hash;
      var $target = $(target);
      $('html, body').stop().animate({
        'scrollTop': $target.offset().top
      }, 900, 'swing', function () {
        window.location.hash = target;
      });
    });
  });
});
  1. Evergreen browsers update to the next version incrementally and automatically without user intervention. Examples include Chrome, Microsoft Edge, and later versions of Firefox.

  2. Dependency injection is a fancy term for providing the objects that an object needs (its dependencies) instead of having it construct them itself. It’s a very useful technique for testing, since it allows dependencies to be mocked or stubbed out. (Source)

  3. In software engineering, a circular dependency is a relation between two or more modules which either directly or indirectly depend on each other to function properly. (Source)