Context require 01 October 2013

This is how I organise JavaScript assets on this site.

view code on github

Motivation

When I started this blog, I knew that I wanted to include specific and varied scripts on each post.

I didn’t want to serve all my js files together in one blob with each page load:

Approach

I use require.js to modularise my code. If I want to make part of the document fancy - then I define it in a file called fancy.js like this:

define(['jquery'], function($){
  return function(element){
    $(element).doFancyStuff()
  }
})

… the module defines a function that can be applied with a particular dom node.

Then, in the markup - I specify which module I want to apply to a particular piece of markup:

<div class="cr" data-cr="fancy">
	<h1>RAINBOWS</h1>
	<p>UNICORNS</p>
</div>

I then stitch this together with another require.js module which looks through the page, loads any modules and applies them appropriately. It looks something like this (I’ve used jQuery here for succinctness):

$('.cr').each(function(){
  var self = this, requirement = $(self).data('cr');

  require(requirement, function(module){
    module(self);
  })
})

Profit

Now I can load only the necessary scripts to display a page; which vary across pages on my blog:

Dogfooding

I’ve enjoyed using this approach - it’s made it really easy to add new posts. I’ve felt like I’ve been writing code rather than tweaking and maintaining it.

It also feels like a good distinction of concerns - by starting with the html/dom I’ve focussed on what I’m trying to enhance with javascript.

Only loading scripts for on-screen elements

This approach kind of sucked for my homepage though - I’ve got all my posts in full, so every single script would be loaded.

So I rewrote my script to defer the loading of a module until the related element is on-screen. It looks something like this (again, jQuery here for brevity):

// using jquery.inview
$('.cr-defer').one('inview', function(){
  var self = this, requirement = $(self).data('cr');

  require(requirement, function(module){
    module(self);
  })
})

I’ve written a way to display the modules as they are loaded, which you can turn on with the button below (if the module loaded okay!)

This should reload the page with a panel to the left which will display:

as you scroll down the page, you should see more modules loading in as you go past the posts.

Limitations / solutions

I can use this approach because I’ve got independent bits of content. Creating larger scale interconnected sites requires a lot more thought and planning. Addy Osami did a great talk on building large scale JS applications at last years jQueryUK. He also has an online book - Learning JavaScript Design Patterns which is worth a read.

The other limitation of this approach is performance. Require.js has a great build tool which lets you compile your components into a single file - though this would defeat the purpose of what I was trying to do in the first place.

The issue isn’t with the size of download, but the waterfall effect that happens when each dependency is loaded (as a module must be loaded before the dependencies can be found). This (and a solution to this) is described brilliantly in a presentation at last years JSConfEU - A novel, efficient approach to JavaScript loading

Also, if you’re interested in this kind of stuff - have a read of Alex Sexton’s blog post about deploying javascript applications.

I have a feeling that this is the kind of problem that people have dealt with or have had ideas about before. I’d really love to hear what you think - ping me on twitter or comment on hacker news.