Smarter Script Loading with LABjs

By  on  

We all know that asynchronous resource loading is the key to preventing unwanted and unnecessary blocking within the browser.  There are many scripts/libraries available to help with async script loading but the ones that succeed are simple, compact, and reliable.  Those words describe Kyle Simpson's LABjs, a lightweight utility for loading your scripts without blocking.

Download LABjs

You can download LABjs at the LABjs website or via GitHub.

Traditional Script Inclusion

Traditional script inclusion blocks subsequent resources from loading.  The following is traditional, blocking script inclusion:

<script src="mootools-1.3.js"></script>
<script src="lightface/Source/LightFace.js"></script>
<script src="lightface/Source/LightFace.Request.js"></script>
<script src="lightface/Source/LightFace.Static.js"></script>
<script src="Color.js"></script>

Wait...wait...wait.  What a waste of time.  Other pieces of the page don't rely on these scripts, so why should users have to wait for these scripts to load before other resources load?  LABjs fixes this mess.

LABjs Usage

LABjs itself needs to be included within the page via traditional SCRIPT tag:

<script src="LAB.js"></script>

The $LAB variable is LABjs' handle.  You can load scripts using the script method:

$LAB.script('mootools-1.3.js');

The wait method allows you to prevent script execution (not loading) before executing subsequent scripts in the chain:

$LAB
.script('mootools-1.3.js').wait()
.script('mootools-dependent-script.js');

Using wait is a great way to manage dependencies.  For example, you cannot load MooTools More before MooTools Core, right?  Even though you direct Core to load first, More may load before Core.  That will cause many, many errors.  You can use wait to prevent that problem:

$LAB
.script('mootools-1.3.js').wait()
.script('mootools-1.3-more.js');

The wait method also acts as a callback for when scripts are done loading:

$LAB
.script('mootools-1.3.js').wait()
.script('mootools-1.3-more.js').wait(function() {
	
	// Now that the Fx.Accordion is available....
	var accordion = new Fx.Accordion(/* ... */);
	
});

To complete the example I started this post with:

$LAB
.script('mootools-1.3.js').wait()
.script('lightface/Source/LightFace.js').wait()
.script('lightface/Source/LightFace.Request.js')
.script('lightface/Source/LightFace.Static.js').wait(function() {
	var modal = new LightFace.Request(/*  */);
})
.script('Color.js')

Using LABjs to load scripts is as simple as that!

LABjs Options

LABjs also provides a bevy of options to customize script loading to your personal needs.  Options may be set via the setOptions method:

$LAB.setOptions({ AlwaysPreserveOrder:true });

A few of the options include:

  • AlwaysPreserveOrder:  Implicitly calls wait() after each script
  • UsePreloading: Allows LABjs to try other loading tricks (trick information here)
  • BasePath: Sets a base path for all scripts

Many other options are available.  You can get more information about LABjs options by reading the documentation.

LABjs + Async FTW

LABjs is an awesome, awesome little utility:  compact, easy to  use, and reliable.  Twitter must agree with me because they are using LABjs.  Big props to Kyle for his outstanding work. Be sure to give LABjs a try; low risk, high reward.

Recent Features

Incredible Demos

  • By
    Create Twitter-Style Buttons with the Dojo Toolkit

    I love that JavaScript toolkits make enhancing web pages incredibly easy. Today I'll cover an effect that I've already coded with MooTools: creating a Twitter-style animated "Sign In" button. Check out this five minute tutorial so you can take your static...

  • By
    Create a Download Package Using MooTools Moousture

    Zohaib Sibt-e-Hassan recently released a great mouse gestures library for MooTools called Moousture. Moousture allows you to trigger functionality by moving your mouse in specified custom patterns. Too illustrate Moousture's value, I've created an image download builder using Mooustures and PHP. The XHTML We provide...

Discussion

  1. WoW.. what a great resource David! Thanks a lot!!

  2. Pozo

    Here is mine just for clicking events https://github.com/Pozo/bismuth

  3. Christian Wisniewski

    Looking good, but wouldn’t it also work just adding the script-tag to the end of the html?
    That way the page would be displayed first, since the Javascript isn’t needed at that time at all..

  4. An hour ago I stumbled upon head.js and now I see a similar LAB.js. I have to compare the options available, because to be honest i was quite exited when I saw those of head.js.

    • there is another similar require.js, LAB.js can not judge whether a script has been loaded already

  5. Hi david,

    Excellent information….

  6. Ikon

    In webkit based browsers there is still an iritating javascript loading que hazard. It doesn’t matter in what order you specify your scripts – even in the header – it will load them as it pleases. Will this LABjs help me in forcing those browsers to “behave”?

    • I’m not aware of the details of that WebKit issue. Is there documentation somewhere I can look at?

  7. Ulli

    Very popular at delicious.com:
    http://headjs.com/

    I’m not a programmer, but the facts and features sound good.

  8. @ikon @david —

    what i believe you’re referring to is that in Webkit (and indeed, also IE), if you dynamically add a bunch of script elements to a page, they will execute in “as soon as possible” order, not maintaining their insertion execution order. am i right?

    if so, you’ve hit on *exactly* why LABjs exists, to smooth out that problem. LABjs goes to great lengths to normalize loading and execution order behavior among the browsers.

    the .wait() in a chain will tell LABjs to make sure that scripts before the .wait() *execute* before scripts after the .wait(). All will still be loaded in parallel, but LABjs uses various tricks to “preload” them without execution, so that it can execute them in the order you specify by using .wait().

    side note: you can take a shortcut and just say you want *all* your scripts to execute in order (instead of adding a .wait() after every single one yourself), by setting the option AlwaysPreserveOrder to true, like this:

    $LAB.setOptions({AlwaysPreserveOrder:true})
    .script(...)
    .script(...)
    .wait(function(){ ... })
    .script(...)
    ...
    ;
    
  9. Diego Pizarro

    I don’t see how different lab.js logic is different from for example

    		new Asset.javascript('js/lang.js', {
    			id: 'lang_js',
    			onload: function(){
    				console.log('loaded');
    			}
    		});
    

    If you wrap the asset calls in a chain you get the desired order don’t you?

  10. @Diego-
    What LABjs does that may not be apparent is that it loads all scripts in the chain in parallel, but executes them in serial order. Just looping over a simple chain of loading calls will result in serial loading rather than parallel, which may be much slower.

    • Diego Pizarro

      Thanks for the reply, I am sure i will give your script a try.
      Excellent!

  11. my preferred script loader is JSLoad. you should check it out.. the biggest difference with LABjs and other loader out there is you can define dependencies from the start, without having to do all those long chaining in all your pages.

    with LABjs you have to write this in every part of your application that needs a lightbox.

    $LAB
    .script('mootools-1.3.js').wait()
    .script('lightface/Source/LightFace.js').wait()
    .script('lightface/Source/LightFace.Request.js')
    .script('lightface/Source/LightFace.Static.js');
    

    with JSLoad you can instead create a central dependency config that can be used everywhere. so you do something like this instead.

    // define dependencies
    var scripts = [
      { name: 'mootools-1.3' },
      { name: 'lightface/Source/LightFace' },
      { name: 'lightface/Source/LightFace.Request' },
      { name: 'lightface/Source/LightFace.Static' },
      ...
      { name: 'somepage',
        requires: [
          'mootools-1.3',
          'lightface/Source/LightFace',
          'lightface/Source/LightFace.Request',
          'lightface/Source/LightFace.Static'
        ]
      },
      ...
    ];
    var jsLoader = new JSLoad(scripts);
    

    and on every page that needs the lightbox, u simply run this:

    jsLoader.load(['somepage'], function() {
      // do stuff
    });
    

    without having to rewrite all those long script chains.

  12. @Rizky – LABjs’ API, the .script() function, accepts an array. You could extremely easily predefine your list of scripts in array, and re-use that, without a big long chain. I’m not sure I see how that’s a weakness of LABjs compared to your loader.

  13. I have a PHP session variable named $_SESSION[‘token’] and assigning this to a javascript variable js_token. This is done in the php page itself within script tags. Now I need to access this variable from a js file that I have loaded via labjs. The problem is that labjs loads all the js files at the top of the header and the variable assigning script is placed after all the js files.

    This creates a problem as I cannot access the js_token variable.
    How can I fix this issue?

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!