Documentation


Overview

The old ways:

<script src="framework.js"></script>
<script src="plugin.framework.js"></script>
<script src="myplugin.framework.js"></script>
<script src="init.js"></script>

In the above example, all scripts load and execute serially (by default).

The new way (before </head>):

<script src="Only.min.js" data-only="init.js" asnyc></script>

In init.js:

$O.js("framework.js").wait()
    .js(["plugin.framework.js", "myplugin.framework.js"])
    .wait(function(){
        // just inline the code to run framework plugins!
    });

In the above example, all scripts load in parallel (by default). "framework.js" will execute first, but "plugin.framework.js" and "myplugin.framework.js" will execute in any order as it is not important (first-come, first-served). The inline code will wait for all 3 scripts to execute before it runs.

How to deal with inline scripts

Only.js philosophy is only one: avoid inline scripts. If your init.js file size is larger than 128kb when minifed and gizipped then you should use a modular framework such as require.js instead.

To convert <script src="..."></script> and <script>/* inline code */</script> tags into $O API calls, use these two rules:

  1. For every <script src="..."></script> tag you are replacing, you should have a .js(...) call.
  2. For every <script>/* inline code */</script> inline script block with code in it, we need a .wait(function(){ ... }) call to wrap around the code.

Don't forget that the order of execution will determine if .wait() call will be used to ensure execution order. If this still is a little confusing, view Only.js examples below.

Special "Feature" Note(s)

Only.js (LABjs) is able to handle non-feature-testable behavior in older legacy browsers through the use of two small but fairly solid browser inference sniffs. This is different from feature-detection (which is always preferable when possible), but with script-loading quirk behavior, there's no way to feature-detect (in those older legacy browsers), so we simply have to fork behavior based on browser family. So, the following two browser inference sniffs are used:

Only.js (LABjs) feature-tests for `async=true` (aka "ordered async") on script-inserted script elements. Read more about this feature on the Dynamic Script Execution Order WHATWG Wiki Page and Henri Sivonen's HTML5 Script Execution Changes.

The HTML Spec has officially been updated to include the "async=false" behavior, that Only.js (LABjs) employs a feature-detect for. FF4+, Webkit (March 2011+), IE10p2+, and Chrome 12+ now have this implemented (and Opera is coming soon).

Only.js (LABjs) also feature-tests for "real preloading". This type of preloading doesn't rely on the "cache preloading" hacks, but instead directly preloads the script (once) and then allows the execution of that code on-demand without a second (cached) request. This functionality has been present in IE since version 4, is suggested in the HTML spec (not required), and has been proposed to be officially declared a requirement. Even if it's never actually standardized any further, it's the best method available for IE, so it's worth it. Read more about the proposal to make this a requirement: Script Execution Control.


Only.js API

Methods



[$O] $O.ready(function inlineScript)

This function is used to defer the execution of a function reference (usually an inline anonymous function) until DOMContentLoaded. This method is not chain-able, and thus must only be called stand-alone like this: $O.ready({...});. It is usually used with $O.test() to ensure the test is executed after the DOM has loaded.

Parameters
inlineScript : (required)
Returns
none


void $O.setDefaults(object optionsObject)

Sets one or more options as global defaults to be used by all $O chains on the page. This method is not chain-able, and thus must only be called stand-alone like this: $O.setDefaults({...}); before any $O chains in init.js.

Parameters
optionsObject : an object which contains name/value pairs for the options to set:

NOTE: The autoPolyfill option can only be used in $O.setDefaults({ ... }). It has no effect in $O.setOptions().

Returns
none


[$O] $O.setOptions(object optionsObject)

Sets one or more options only to be in effect for the current $O chain being executed. This method is chainable (in fact, for it to have any effect, it must be part of a chain!), but it must be the first call in the chain (as changing many of the options mid-chain would create race conditions). So, it must be called like this: $O.setOptions({...}).js(...)...;

Parameters
optionsObject : an object which contains name/value pairs for the options to set
Returns
$O : the chained object reference so subsequent calls to test(), js() and wait() can be made.


[$O] $O.test(selector)

This function is used to test if one or more selectors exists on the current page.

Parameters
varies (a valid selector string or an array of valid selector strings):

Returns
$O : the chained object reference so subsequent calls to test(), js() and wait() can be made.


[$O] $O.js(varies,...)

This method accepts one or more parameters of varying types. Each parameter value is intended to specify a script resource URI to load.

Parameters
varies (can be any number/combination of the following):

Returns
$O : the chained object reference so subsequent calls to test(), js() and wait() can be made.


[$O] $O.wait(function inlineScript[=null])

This function serves two purposes. Firstly, when inserted into a chain, it tells the chain to ensure that all scripts previously listed in the chain should finish executing before allowing the internal 'execution cursor' to proceed to the remaining part of the chain. Essentially, you can specify "blocking" behavior in terms of execution (not loading!) to ensure dependencies execute in the proper order (regardless of how they were loaded).

Secondly, you can specify a function reference (usually an inline anonymous function) that will be executed in order, immediately following the chain's previous scripts executing, but before subsequent scripts in the chain are executed. This is synonymous with inline <script> tags before or after <script src="..."> type tags.

For instance, it can be used to defer the initialization execution of a script you are downloading, like this: $O.js("script.js").wait(function(){initScript();}); where initScript() is a function that is defined in "script.js".

Parameters
inlineScript : (optional, defaults to null)
Returns
$O : the chained object reference so subsequent calls to test(), js() and wait() can be made.


[$O instance] $O.noConflict(none)

noConflict() rolls back the page to the previous version of Only.js (if any) before this copy of Only.js was loaded, and returns the current instance of $O.

Parameters
none
Returns
$O instance : the current $O instance (from before the rollback)


[$O instance] $O.end(none)

end() creates a new chain-able instance of $O. This allows you to get new chain-able instances of Only.js without breaking a chain. It is different from sandbox() that returns a clean instance (does not handle test() chains properly) and might be problematic on long test() chains. Mostly used with $O.test() without breaking the visible chain.

Parameters
none
Returns
$O instance : a new chain-able instance of $O


[$O instance] $O.sandbox(none)

sandbox() creates a new clean instance of $O. This allows you to get new instances of Only.js.

Parameters
none
Returns
$O instance : a new clean instance of $O


Code Examples

Example 1:

$O.js("script1.js")
    .js("script2.js")
    .js("script3.js")
    .wait(function(){ // wait for all scripts to execute first
        script1Func();
        script2Func();
        script3Func();
    });

Example 2:

$O.ready(function(){ // execute after DOMContentLoaded
    $O.js({
        src: "script1.js",
        type: "text/javascript",
        attributes: { "gumby-init" : "false" }
    })
    .js("script2.js")
    .js("script3.js")
    .wait(function(){ // wait for all scripts to execute first
        script1Func();
        script2Func();
        script3Func();
    });
});

Example 3:

// test if #scriptsBox selector exists
$O.ready(function(){
    $O.test('#scriptsBox')
        .js("script1.js", "script2.js", "script3.js")
        .wait(function(){ // wait for all scripts to execute first
            script1Func();
            script2Func();
            script3Func();
        });
});

Example 4:

// test for #scriptsBox selector, if it exists then the chain is executed
$O.ready(function(){
    $O.test('#scriptsBox')
        .js(["script1.js", "script2.js"], "script3.js")
        .wait(function(){ // wait for all scripts to execute first
            script1Func();
            script2Func();
            script3Func();
        }).end() // implicitly break current test() chain
        .js("script4.js") // will execute if #scriptsBox selector does not exist
        .wait(function(){script4Func();});
});

Example 5:

$O.ready(function(){
    $O.test('#scriptsBox, #pluginsBox') // OR-like selector test; execute if any were found
        .js("script1.js").wait() // empty wait() simply ensures execution order be preserved for this script
        .js("script2.js") // both script2 and script3 depend on script1 being executed before
        .js("script3.js").wait() // but are not dependent on each other and can execute in any order
        .js("script4.js") // depends on script1, script2 and script3 being loaded before
        .wait(function(){script4Func();});
});

Example 6:

$O.ready(function(){
    $O.test('#scriptsBox').test('#pluginsBox') // AND-like selector test; execute if both were found
    // also equivalent to: $O.test(['#scriptsBox', '#pluginsBox'])
        .js("script1.js") // script1, script2, and script3 do not depend on each other,
        .js("script2.js") // so execute in any order
        .js("script3.js")
        .wait(function(){  // can still have executable wait(...) functions if you need to
            alert("Scripts 1-3 are loaded!");
        })
        .js("script4.js") // depends on script1, script2 and script3 being executed before
        .wait(function(){script4Func();});
});

Example 7:

$O.setOptions({alwaysPreserveOrder:true}) // tells this chain to implicitly "wait" on
    // execution (not loading) between each script
    .js("script1.js") // script1, script2, script3, and script4 *DO* depend on each
    .js("script2.js") // other, and will execute serially in order after all 4 have have
    .js("script3.js") // loaded in parallel
    .js("script4.js")
    .wait(function(){script4Func();});

Example 8:

$O.js(function(){
        // assuming `_is_IE` defined by host page as true in IE and false in other browsers
        if (_is_IE) {
            return "ie.js"; // only if in IE, this script will be loaded
        }
        else {
            return null; // if not in IE, this script call will effectively be ignored
        }
    })
    .js("script1.js")
    .wait();