Its been a long while since my last post, which is mostly due to the amount of work projects going on.

Of the many interesting things I would like to blog about the first one that comes to mind is what we’ve been doing with our client side javascript lately. Javascript can feel like the wild wild west sometimes with the way that it gets haphazardly strewn about littering the global namespace with who knows what variables and generally just being a giant mess that gets harder to maintain over time.

Most people are at least using a modular approach with something along the lines of:

(function() {
 //export something useful to global namespace
})()

But what do you do when you start to get a lot of these, and they start to interact with each other in ways such that certain modules are dependent on others being there or having to juggle whether or not you’ve already loaded it so as not to have to load it again, etc…

Well gone are those days, enter Require.js and a civilized approach to organizing your client side javascript into modules that can be easily managed and comes with the perk of an optimization tool which enables you to combine all dependencies for a page into practically one single minified/obfuscated file to get screaming fast load times with far fewer requests that need to be made to bring down your content.

So not only am I going to describe a possible approach to getting set up, but I’ve already got it running on this page right now so feel free to open your web development tool of choice be it FireBug or Chromes Javascript console and take a look for yourself. It goes without saying that if you are using Internet Explorer you should seriously reconsider your web browsing habits.

So first things first, the idea of using require is that you include a snippet such as this up in your head tag.

<script type="text/javascript">
  var require = {
    priority: ['jquery-1.6.min', 'underscore']
  };
</script>
<script type="text/javascript" data-main="/js/main" src="/js/require.js"></script>

This is but one of many possible ways to set this up, but it’s my current preferred approach. Dependencies that you know you are going to want before any of your modules even try to load can be specifed in the priority array. The data-main attribute lets require know which javascript file you want to be the main entry point aka the require bootstrapper as I like to call it. Note that you leave off the .js extension.

main.js

require(['util/module-activator', 'util/analytics'],
  function(moduleActivator, analytics){
    analytics.track('UA-17910584-1');
    moduleActivator.execute();
  });

The example above takes in an array of dependencies as a first parameter, and a function which will make use of those dependencies as its second parameter.

Not every dependency needs to be included as a parameter to the function. If all you need to do is just execute some arbitrary javascript or link to a .js file that is not defined in the require syntax then feel free to leave it out of the parameters to your function however anything like that will need to be at the end of your dependency array as the arguments to the function are passed in the order they were specified.

The two modules you see me using here are great examples of some of the things you can get done using require. Lets take a look at them.

util/analytics

define(function(){
    var exports = {
      track: function(accountId) {
        var _gaq = window._gaq = _gaq || [],
            ga = document.createElement('script'),
            s = document.getElementsByTagName('script')[0];

        _gaq.push(['_setAccount', accountId]);
        _gaq.push(['_trackPageview']);

        ga.type = 'text/javascript';
        ga.async = true;
        ga.src = ('https:' == document.location.protocol ?
          'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';

        s.parentNode.insertBefore(ga, s);
      }
    };

  return exports;
});

You may recognize this as the google analytics script, but rewritten according to my interpretation of current best javascript practices gleaned from Javascript: The Good Parts which if you haven’t read yet I would recommend that you do.

util/module-activator

define(function(){
  var module = {};

  function loadModule(domElement) {
    var element = $(domElement),
        moduleName = element.data("module");

    require([moduleName], function(module) {
      module.init(element);
    });
  };

  module.execute = function(element) {
    var element = element || $("html"),
        dataModules = $("[data-module]", element);

    _.each(dataModules, loadModule);
  };

  return module;
});

The module activators power is second only to its mystery. It scrapes the page for any elements with a data-module attribute on them and issues a require call to load a module by the name of the value provided and then invokes an init function on the module passing it the element as a jquery wrapped set. The element can then be used to scope selectors against inside your module using jquery or put to any purpose you can think of.

Step 1: Add a data-module attribute to an element on page:

<div id="color-changer" data-module="color-changer"></div>

Step 2: Enjoy your functionality. Click the div below and watch it change colors.

The implementation of color-changer looks like this:

define(function() {
  var exports = {},
      colors = ['red', 'blue', 'yellow', 'green', 'orange', 'purple'],
      getRandomColor = function() {
        var max = colors.length,
            index = Math.floor(Math.random()*max+1);
        return colors[index];
      };

  exports.init = function(element) {
    var previousColor;
    element.click(function() {
      var color;
      while (!color || color === previousColor) {
        color = getRandomColor();
      }
      element.css('background', color);
      previousColor = color;
    });
  };

  return exports;
});

To demonstrate this modules reusability look back up at Step 2 and try clicking on the word “div”. The markup for it looks like this:

<span data-module="color-changer">div</span>

It will also change colors, and if you notice the two different color changers keep their own state of previous color to ensure that the next random color selected is not the same as the one before.

I hope this shows you some of the potential an approach like this can have and gets you excited about using require.js for yourself.

blog comments powered by Disqus