i am dustin diaz

a JavaScriptr...

boosh.

don't worry about it.

$script.js - Another JavaScript loader

God forbid a JS utility suffixed with 'another' in the title. But there was no other option. Thus introducing $script.js: an asynchronous JavaScript loader and dependency manager with an astonishingly impressive lightweight footprint of only 643 BYTES! (yes, you read that correctly). Like many other script loaders, $script.js allows you to load script resources on-demand from any URL and not block other resources from loading (like CSS and images). Furthermore, it's unique interface allows developers to work easily with even the most complicated dependencies, which can often be the case for large, complex web applications.

A large portion of this utility was inspired by the selflessness research on web optimization from Steve Souders ( @souders ), and the amazing work that has gone into LAB.js by Kyle Simpson ( @getify ).

But let's not get ahead of ourselves, before you keep reading, it might be a good time and reflect on some of Kyle's insightful thoughts on script loaders, whom also points out Souders ControlJS and Tero Piirainen's head.js, especially given that his conclusion states instead of creating more and different script loaders, I think a better use of our time would be to consolidate efforts in getting the browsers and the spec/W3C to give us a reliable native mechanism for managing dynamic script loading. I agree, and also, sorry.

The core

At the heart of $script.js, it pulls from the core of what we've all known how to do for years.

var el = doc.createElement("script"),
loaded = false;
el.onload = el.onreadystatechange = function () {
  if ((el.readyState && el.readyState !== "complete" && el.readyState !== "loaded") || loaded) {
    return false;
  }
  el.onload = el.onreadystatechange = null;
  loaded = true;
  // done!
};
el.async = true;
el.src = path;
document.getElementsByTagName('head')[0].insertBefore(el, head.firstChild);
Also despite this looking like something we've all could have written, the logic is borrowed from @getify's gist to load LAB itself.

The interface

The most attractive aspect of $script.js is the external interface. Let's look at the two most basic examples.
$script('analytics.js');
$script('library.js', function() {
  // do stuff with library.
});

Dependency Management

Moving along, it allows for two styles of dependency management. The first was already demonstrated with simple callbacks. Here is an example of what multiple dependents might look like.
$script('yui-base.js', function() {
  // do stuff with base...
  $script(['yui-anim.js', 'yui-connect.js'], function() {
    // do stuff with anim and connect...
  });
  $script('yui-drag.js', function() {
    // do stuff with drag...
  });
});
Of course, for the functional crowd, this is very nice. But it becomes increasingly difficult when more dependencies are introduced throughout the state of an application, and other dynamic loading techniques like lazy loading are blocked by this functional nature. So to resolve this, an id based observer approach is supported. It looks like this.

$script('jquery.js', 'jquery');


$script.ready('jquery', function() {
  // do stuff with jQuery
});
The nice thing about this is that you can have multiple calls to 'ready' for the same dependency. This would allow you to write a bunch of dependent files, and still load them asynchronously! Imagine this common scenario. You're writing a jQuery plugin, but your plugin file can't load until jQuery loads. That could look a few different ways
old school - blocks CSS, Images, AND JS!
<script src="jquery.js"></script>
<script src="my-jquery-plugin.js"></script>
<script src="my-app-that-uses-plugin.js"></script>




middle school - loads as non-blocking, but has multiple dependents
$script('jquery.js', function() {
  $script('my-jquery-plugin.js', function() {
    $script('my-app-that-uses-plugin.js');
  });
});




new school - loads as non-blocking, and ALL js files load asynchronously
$script('jquery.js', 'jquery');
$script('my-jquery-plugin.js', 'plugin');
$script('my-app-that-uses-plugin.js');


/*--- in my-jquery-plugin.js ---*/
$script.ready('jquery', function() {
  // plugin code...
});


/*--- in my-app-that-uses-plugin.js ---*/
$script.ready('plugin', function() {
  // use your plugin :)
});
Last but not least, as you might have already seen, you can group dependencies together by name, as well as request multiple by name. It's best illustrated with more examples, but take special note that this is an advantageous feature that compliments generic programming practices and code-reuse, and eliminates the need for dependency assumptions. Have a look.
var dependencyList = {
  foo: 'foo.js',
  bar: 'bar.js',
  thunk: ['thunkor.js', 'thunky.js']
};


$script('foo.js', 'foo');
$script('bar.js', 'bar');


$script.ready(['foo', 'bar', 'thunk'], function() {
  // foo.js & bar.js & thunkor.js & thunky.js is ready
}, function(depsNotFound) {
    // did not find 'thunk' dependency (which has thunkor.js and thunky.js
    // so lazy load them now
    depsNotFound.forEach(function(dep) {
      $script(dependencyList[dep], dep);
    });
  });

Wrapping it up

There's not much else to it. If you dig the interface and it works with your style of development - have at it.

Github: $script.js project

Download: $script.js [2.5k] full source, un-minified

Download: $script.min.js [643b] min + gzip

Final note on 'other script loaders'

In particular I wanted to call out the great work done on LAB.js - $script.js is not better or worse. And it is most certainly different. LAB attempts to do some nice cross-browser speed improvements (like downloading via xhr and other crazy browser forks). Hell, we use it here at Twitter, and we love it. The point of $script.js is to get back to the basics of loading script's w/o blocking other resources, which is where you'll find the most win. Furthermore $script.js is fancied with a nice observer interface to make dependency management easier paired with fail-safe callbacks to allow for lazy loading techniques.

Last but not least, it's very small (heh, it's how you use it ;) allowing you to simply embed the core directly in your html source. Cheers.

this is who i am

Hi, my name is Dustin Diaz and I'm an Engineer @ObviousCorp. Previously @Twitter, @Google, and @Yahoo, author of Strobist® Info co-author of JavaScript Design Patterns, co-creator of the Ender JavaScript Framework, a Photographer, and an amateur Mixologist. This is my website. Welcome!

On this site I write about JavaScript. You can also follow along with my open-source work on Github.

This site is optimized and works best in Microsoft Internet Explorer 6.