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.