Get Started


Description

This project is a simple little tool for being able to dynamically load javascript files. It's like many script loaders where the goal is to improve the speed of page load by allowing scripts to load in parallel and preserve execution order for dependencies. The thing it does differently than most others is it allows you load dependencies based on what selectors exist on the page. By default:

What most loaders fail to do is:

Why use Only.js


Normally, I'd prefer call scripts: plugins, mostly because they are used to enhance the UI. I'd also prefer not to write all plugin script tags in the HTML (Eg: Google analytics code) cause I believe they depend on something the user needs to use on the webpage (Eg: a time picker plugin). Watch the following closely:

Using the default "<script> tag structure" we were taught:

<!-- why must these scripts download and execute serially? -->
<script src="/assets/plugins/jquery.min.js"></script>
<script src="/assets/plugins/morris/morris.min.js"></script>
<script src="/assets/plugins/jquery.sparkline.min.js"></script>
<script src="/assets/js/init.js"></script>
<!-- Oh, this inline script cannot be cached; one less HTTP request (which is better?) -->
<script>
    jQuery(document).ready(function() {
        init();
    });

    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
    })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

    ga('create', 'UA-15567888-7', { 'name': 'only', 'cookieDomain': 'none' });
</script>

With LABjs (or similar script loaders):

<script src="LAB.min.js"></script>
<!-- Another mightier inline script which cannot be cached; also clutters the DOM -->
<script>
    $LAB.script("/assets/plugins/jquery.min.js").wait()
        // but morris and sparkline might not always be needed together
        // (need to think of something... maybe server-side exclusion?)
        .script("/assets/plugins/morris/morris.min.js")
        .script("/assets/plugins/jquery.sparkline.min.js")
        .script("/assets/js/init.js").wait(function(){
            // must I use jQuery ~90kb if I just need cross-browser DOMContentLoaded detection?
            jQuery(document).ready(function() {
                init();
            });
        });

    // I only need analytics for site visitors not logged in users (need to think of something...)
    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
    })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

    ga('create', 'UA-15567888-7', { 'name': 'only', 'cookieDomain': 'none' });
</script>

With Only.js (a selector-based loader):

<script src="Only.min.js" data-only="init.js" asnyc></script> <!-- what can I say? -->

In init.js:

$O.ready(function(){
    // will execute if '#morris' is found
    $O.test('#morris').js("/assets/plugins/morris/morris.min.js")
        .wait(function(){
            Morris.Area({element: '#morris'});
        });

    // will execute if '#sparkline' is found
    $O.test('#sparkline').js("/assets/plugins/jquery.min.js").wait()
        .js("/assets/plugins/jquery.sparkline.min.js").wait(function(){
            $('#sparkline').sparkline(); // no need for jQuery(document).ready() !!!
        });

    // I only need analytics for site visitors not logged in users
    $O.test('body.not-logged').wait(function(){
        (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
        m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
        })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

        ga('create', 'UA-15567888-7', { 'name': 'only', 'cookieDomain': 'none' });
    });
});

All these features and more... for just ~2.5kb (minified and gizipped)!. Why haven't I been using this?

Why was Only.js built on LABjs


I use script loaders (LABjs only) for high performance websites built by myself and in my company. There are various existing script loaders but LABjs excels them all both in speed, and stable parallel resource (scripts, stylesheets, and images) loading. I stress-tested 5 script loaders — LABjs passed all.

From LABjs Docs:

LABjs lets you load pretty much any script file, whether you control it or not, with no intrusion or convention for dependencies, other than the order and blocking that you define. It keeps track of what you've asked for and what has downloaded, and lets you only define a handler once for a group of scripts that will execute together in parallel. The API style (with chaining) makes is very easy to convert a set of script tags in your page into code to load them, without having to worry that race conditions will cause issues for scripts loading in the wrong order if there are explicit dependencies involved.

When to use Only.js


If you always have two or more dependencies per selector; eg: DatePair.js and jQuery.datepair.js are always needed to initialize a datepair in .datepair — combine and minify them. This will reduce the number of HTTP requests and generally create a better loading experience for the website users as it will make your page load faster and more reliably.

What if I need to load CSS


The aim of this project is to simplify and speed up javascript loading. However if you need to also load stylesheets (like I usually do) for related javascript functionality/plugins, then there's a sister project dedicated to asynchronous stylesheet loading ie, CSS.load.

You can include CSS.load and use it like:

$O.js("/assets/js/CSS.load.min.js"); // start loading CSS.load right away!
$O.ready(function(){
    // will execute if '#sparkline' is found
    $O.test('#sparkline')
        .js("/assets/plugins/jquery.min.js", "/assets/js/CSS.load.min.js") // depend on CSS.load
        // (will wait for first call to CSS.load.min.js to download and execute if allowDuplicates is false)
        .wait(function(){
            // $O.test() is successful
            // download sparkline stylesheet asynchronously ASAP
            $CSS().load("/assets/plugins/jquery.sparkline.min.css");
            // the stylesheet will arrive faster than in the next wait()
        })
        .js("/assets/plugins/jquery.sparkline.min.js").wait(function(){
            $('#sparkline').sparkline(); // no need for jQuery(document).ready() !!!
        });
});

Or simply:

$O.ready(function(){
    // will execute if '#sparkline' is found
    $O.test('#sparkline')
        .js("/assets/plugins/jquery.min.js", "/assets/js/CSS.load.min.js").wait()
        .js("/assets/plugins/jquery.sparkline.min.js", function(){
            $CSS().load("/assets/plugins/jquery.sparkline.min.css"); // ensure you return nothing (read the docs! :)
        }).wait(function(){
            $('#sparkline').sparkline(); // no need for jQuery(document).ready() !!!
        });
});

When not to use Only.js


Only.js can and should be used on virtually any site but it shouldn't be used for loading a couple of special file types Eg: <script type="text/html"><script> (you could test them with our custom attributes though if they'll work for you).

Scripts using document.write()

Don't use Only.js with scripts that use document.write(). document.write() is natively synchronous but there is a replacement that operates the same way but is async safe, called "DomWrite". DomWrite, used in conjunction with Only.js, will allow you to safely load scripts with document.write() in them via Only.js.

Scripts with bad DOM-ready detection

Some scripts that do DOM-ready detection are flawed in one specific case: when it's loaded in a page AFTER the DOM-ready has already happened but not able to detect that this is the case, in some browsers, and so wait "forever" thinking they are still waiting for DOM-ready to occur. This means that any code which is queued up waiting for DOM-ready to occur never gets executed. Example of such scripts is jquery 1.3.2 and below. Such scripts should not be loaded with Only.js because of the race condition between script loading and the actual DOM-ready event.

If you detect such a script and you'll need to use it without modifying it's source, simply load ONLY the script file manually, using a regular script tag (best before including Only.js). This will guarantee that its internal DOM-ready detection will occur correctly.