Sunday, March 23, 2014

When scripts should run?

A quick walkthrough on when executing a script in the browser.

A lot of examples on the internet show to wrap the code inside the jQuery "ready" event handler. It is not always the case. As a matter of fact you can execute a script in three different fashions:
  • immediately
  • in the ready event handler
  • in the load event handler
  • under another event handler (but this is a different story though)

Immediately

You can execute a script immediately if your script doesn't rely on the DOM, or rely on a fragment of DOM that is already been loaded (because is placed before your script).
I think this is often the preferred case.

JQuery ready

The jQuery ready event handler uses the  domcontentloaded event but is guaranteed to be executed right away if this event is already been fired.
"The handler passed to .ready() is guaranteed to be executed after the DOM is ready"
You should execute your script here if it runs before the fragment of DOM targeted by the script. Given this, if you are loading synchronously you should definitely put the script at the bottom of the HTML and just execute it immediately.

On load

There are rare cases when you need the CSS and images loaded before executing the script. A suggestion: avoid it if you can! For example you could (and definitely should) add the width and height attribute on any img and you can use a bit of inline css styling (in some edge case).
In the few cases you really need this you can use the load event handler.

Asynchronous loading

If you use requirejs you really don't have a clue on when your script is executed. Luckily enough executing it immediately or inside a jQuery ready event is always safe. It's not so using the load event.
Your script could be loaded after the load event is already been fired. For this reason is a good idea to check it and, in case, executing the script immediately.

function mycode(){
   ...
}

if (document.readyState == "complete"){
    mycode();
}
else {
    $(document).load(mycode);
}

Ajax fragments

When you load or create a new fragment of html sometimes you need to run the some code against the new fragment. This can be avoided the vast majority of time using event delegation adding your event handlers to a wrapper node (or to the document itself). If you really need to manage this I suggest to generate a custom event to manage these cases. For example:

// loading a fragment
var $result = $("#result")
$result.load("ajax/test.html", function (){
    ...
    $result.trigger('nodeloaded');
    ...
});

...
// trigger on the document ready 
// for the whole document
$(document).ready(function (evt){
    $(document).trigger('nodeloaded');    
});

...

// manage the event
$(document).on("nodeloaded", function (evt){
    $('.your-class', evt.target).yourplugin();
});

You can find something similar used in jquery mobile.


Sunday, January 26, 2014

Requirejs: edge cases


I have already written about requirejs in this post.
In this new post I'm going to explain something you should know using require in complex environments.

You definitely should use the optimizer

Not using the optimizer in a production environment is highly discouraged.

To explain why, it can be useful thinking about how requirejs works:
All the modules together form a dependency tree. The process of loading this tree is not completely parallel because requirejs is aware of specific dependencies only after loading a module.
Requirejs optimizes this process executing a module only once and caching its result.
The optimizer simply merges a group of modules in a single file and this makes the process much faster.

Inline scripts

Sometimes you'll need to add online scripts to the HTML. Doing this you should consider that your module could be not loaded yet. To fix this problem you can "require" your bundle as a dependency before running the code:

require(["app"], function (){ // app is the name of the bundle
    require(["yourdependency"], function (){
        //inline script
    });
});

But there is another problem!
Many examples shows to put your requirejs configuration inside the first module.
Sometimes you'll need a specific configuration to load your module (for example the baseUrl). But the configuration could not be available in your inline scripts (it's async!).

There is a trick for this: requirejs allows to load the configuration in a synchronous way. You just need to define a global object "require" with the configuration right before loading requirejs script.

<script>
var require = {
    "baseUrl": "http://example.com/js/lib",
    "paths": {
      "app": "main/app"
    },
    "shim": {
        ...
    }
};
</script>
<script data-main="scripts/main.js" src="scripts/require.js"></script>

Mixing synchronous and asynchronous scripts

Performance wise all the scripts should be loaded asynchronously but real world applications need some compromise.
A typical case is using the infamous document.write that wipes out the page when executed asynchronously.

In this slideshow @slicknet explains why loading scripts in different positions.

But mixing synchronous and asynchronous script can be tricky. How can do it safely?

First of all be careful where you load the requirejs script in the page. Many libraries check for the presence of the "define" global variable to be loaded asynchronously (using the UMD pattern):

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['b'], factory);
    } else {
        // Browser globals
        root.amdWeb = factory(root.b);
    }
}(this, function (b) {
    //use b in some fashion.

    // Just return a value to define the module export.
    // This example returns an object, but the module
    // can return a function as the exported value.
    return {};
}));


Jquery for example is AMD compatible so, if you load requirejs before jquery, this will behave in a different way.
My suggestion is to concatenate all the synchronous files of your application in a single script, putting requirejs in the last position.

Second problem: what happen if you are requiring a module and, in this particular page, this module is already loaded synchronously ?
Requirejs will download the module again, this time as a module. This is not ideal so you need to trick requirejs prepending to our main module something like this:

define('jquery', [], function() { // jquery is loaded synchronously before this code
    return jQuery;
});

With the previous code requirejs will use the global jquery instead of requiring a new jquery module to the server.
I am not advocating to load jQuery syncronously. This is just an example!
Another issue: the optimizer will include all the scripts that in your particular case are synchronous.
You need to exclude these files from the optimization (this is an example using the grunt-contrib-requirejs task):

    requirejs: {
      dev: {
        options: {
        "appDir": "www",
        "baseUrl": "js/lib",
        ... other options ...
        "modules": [
            {
                "name": "app",
                "exclude": [
                    "jquery",
                    ... other exclusions ...
                ]
            },
            ... other bundles ...
          ]      
        }
      },

Loading dynamically

In some case you'll want to download a different group of modules in a more dynamic way. Based on a certain configurations or user event (like in this example)
For doing this you can create one or more different bundles (with the optimizer).
You need to remember to exclude from the bundles the resource that should be already loaded in the page (use the previous option).

Scripts not bundled

In some case you don't want to include your file in a bundle because is served by a CDN or directly from your application (for example socket.io). The requirejs optimizator uses the "empty:" option to manage this:

    requirejs: {
      compile: {
        options: {
          mainConfigFile: "static/js/main.js",
          baseUrl: "static/js",
          name: "main",
          paths: {
            'socketio': 'empty:',
            'backboneio': 'empty:'
          },
          out: "static/js/main-built.js"
        }
      }
    }

Requirejs is a very good choice to manage scripts loading and dependencies. I hope to have brought light in some of the dark spots of using this in a complex environment.

Friday, October 18, 2013

Reusable javascript modules with Bower

In a previous post I have used require.js to download javascript files and optimize them.
Here I'll introduce a useful tool to manage your javascript and css assets. But first of all let's talk about modules definition in Javascript.

Universal module definition

At the moment there is no standard way to define a module (we are waiting for EcmaScript6) but you can easily define your module in a way compatible with AMD, UMD and regular script import. Just pick one of these snippets. I recommend to use one of those because declaring object in the global namespace is not a good practice and libraries like requirejs can help to load and optimize your scripts.

Bower

Bower is a package manager designed for the web. You can install it using:

npm install -g bower

And then start downloading your first package (jquery for example):

bower install jquery

You'll find jquery in the jquery folder inside the "bower_components" folder. Bower packages are versioned so you could (and should) specify what version install.
bower install jquery#1.10.2
If this package has some dependencies (other bower packages of course) those will be installed in the same "bower_components" folder. For those who are using node.js: be aware that it is quite different from how npm works! In this case all the dependencies are resolved and flatten in the same folder.

Bower package

A bower package, in fact, is a simple folder with a bower.json file:
{
  "name": "mypackage",
  "version": "0.0.1",
  "main": [
     "script.js",
     "style.css"
  ],
  "homepage": "https://github.com/User/myproject",
  "authors": [
    "Donald Duck "<donald.duck@gmail.com>";
  ],
  "description": "an example package",
  "keywords": [
    "js",
    "css",
  ],
  "license": "private",
  "private": true,
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "lodash": "~1.3.1",
  }
}
Let's see the most important fields:

name: the name of the package

version: the version of the package. It should be the same of the tag on git.

main: these are the main files of your project. For example a jquery repository has a lot of stuff (unbuilded file, test, fixtures etc.) but the main file is just jquery.js.

dependencies: an array of bower packages needed to run this one. You should always specific the version using this syntax.

Bower repository

The published packages are just git tags with a bower.json. The bower command  anyway uses a central repository to store the metadata (name, url of repository, keywords etc.). This repository is on https://bower.herokuapp.com/. If you have a public project you can publish on the central repository using:
bower register mypackage https://github.com/user/mypackage.git
The versions available will be taken from the available tags on you repo.

Running your own repository

Let's say you are working on a project and you don't want to share every package you are working on.
You can easily set up your own bower registry and configure bower to use it. You can do this putting a ".bowerrc" in your working directory (or in your home):
{
    "registry": {
        "search": ["http://bower.myserver.com", "https://bower.herokuapp.com/"],
        "register": "http://bower.myserver.com"
    }
}

This instructs bower to download packages from your server or the official one (the first has the priority), and to register the packages on your server only.

Automate everything !

Using grunt-bower-task you can create a build step that install all the dependencies required by your bower.json and copy all the files defined in "main" in a folder. If you use require.js you can also use grunt-bower-requirejs to build  the require.js configuration automatically.

I think this is enough to start working with bower. Enjoy!