Bootstrap using “YUI Loader” and “YUI Get Utility”

Everyday more and more developers use on-demand loading to improve performance and round “trip” times for web applications and even for traditional web pages that require a certain amount of JavaScript code.

In YUI 2.x, we have two components that can help us to define on-demand rules. These are the YUI Get Utility, and the YUI Loader.

YUI 3.x is a different story, this new version is based on the on-demand philosophy, and because of that, Loader and Get Utility are part of YUI Core.

About this article:

In this article, I will focus on YUI 2.x and specifically, how you can design your application to use the YUI on-demand capabilities in such a way that the transition to YUI 3.x will be straightforward. The main goal is to describe how we can use YUI to organize the code and learn how to deal with on-demand loading processes rather than focus on performance improvements. If you are looking for a performance boost, certainly this technique can help, but I will recommend you to follow the links provided at the bottom of this article.

From my point of view, there are two different types of web applications or pages:

  • Monolithic Applications:
    The application and all its features/behaviors are controlled by a single object/script. Modifications in the application usually impact the core and the application in general.
  • Granular Applications:
    The application is based on regions/webparts that represent certain areas. Usually some of these regions/webparts can be switched or removed from the application without impacting the rest of the functionalities.

I believe the second option (granular applications) is the way to go, especially for more complex applications. Also, this option will most likely make use of on-demand loading, where each region/webpart should be able to load its requirements and execute its own initialization process when it’s requirements become active and it’s DOM structure becomes available in the DOM.

Note: regions/webparts are atomic units used to build each page. Usually they are represented in the DOM as a simple DIV, and are contained as a granular part in the application. From now on, I will refer to these elements as “regions”.

That said, lets see a couple of diagrams to analyze what is the gain behind the on-demand loading process. These are manually created diagrams representing the loading and execution process for a page.

Application timeline:

These two diagrams represent a page/app in which, for the sake of example, contain only two regions (left-column-renderer and ad-renderer).

Traditional process: inserting tags within default DOM structure of every page.

Default Browser Loader: Loading and Execution Timeline

Default Browser Loader: Loading and Execution Timeline

Notes for this diagram:

  • it represents a generic app timeline for a simple page with a bunch of script tags.
  • the scripts can be at the top (in the head) or at the bottom of the page (before </body>), and the sequence is more or less the same.
  • yui-modules” represent a set of YUI modules (probably using YUI Combo Handler to include them all using a single call.
  • bubbling” is a 3rd party module that cannot be loaded thru the YUI Combo Handler.
  • “swf-object” is a well-known 3rd party component to handle flash movies, and it represents another requirement.
  • …other…” represents other regions in the application.

On-demand loading process: using YUI Loader to load the requirements and YUI Get Utility to execute the initialization processes for each region.

Loading and Execution Timeline

YUI Loader and Get Utility: Loading and Execution Timeline

Notes for this diagram:

  • we use some of the elements described in the previous one.
  • “yui-loader” is a small file (only 9.4KB) and it packages Yahoo Global Object, Get Utility, and YUI Loader engine.
  • “boot” is usually a tiny file with the application logic, the definition of the 3rd party modules (swf-object and bubbling), and the list of other dependencies (initializers and renderers) that should be loaded thru YUI Get Utility (like left-column-renderer, ad-renderer, etc).
  • items at the same level can be executed at the same time (JavaScript is a single execution thread, and so, the first to become ready will be the first to be executed).
  • swf-object was transformed into a YUI Module to fit this example (it can be also be treated as a component if you want to).
  • items in the 3rd block (left-column-renderer, ad-renderer, etc) will be included dynamically by the bootstrap definition.

Lets try to identify differences between these two approaches:

The first difference that you may notice in these two diagrams is the “domready” event. It will be more likely to arrive first in the second approach because those scripts will be injected on-demand without blocking the loading process. It could arrive even before the “bootstrap ready” mark, depending on the complexity (size) of the DOM structure.

In theory, the bootstrap is responsible in defining what should be loaded and executed after the “bootstrap ready” moment occurs. This means that we can control everything at the JS level instead of depending on the default DOM structure. So all the logic will be at the JS layer and we should be able to delay certain initialization processes, or switch between them based on the user preferences, etc. All this granularity will be handled at the JS level.

Lets see some code:

Entry Point (index.html):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
 "http://www.w3.org/TR/html4/strict.dtd"&gt;
<html>
<head>
    <title>Bootstrapping for a Web App using YUI 2.x</title>
    <script type="text/javascript" src="bootstrap.js"></script>
</head>
<body class="yui-skin-sam">
    <p>Web App.</p>
    <div id="mod-left-column">
      ...
    </div>
    <div id="mod-content">
      ...
    </div>
    <div id="mod-ads">
      ...
    </div>
</body>
</html>

Bootstrap script (bootstrap.js):

/
  Encapsulation Pattern: Conjuring YUI from thin air (by Chris Heilmann)
  http://www.wait-till-i.com/2008/08/02/conjuring-yui-from-thin-air/
 /
YAHOO_config = function() {
    / injecting the YUI Loader in the current page /
    var s = document.createElement('script');
    s.setAttribute('type', 'text/javascript');
    s.setAttribute('src',
        'http://yui.yahooapis.com/2.7.0/build/yuiloader/yuiloader-min.js'</span><span style="color: #009900;">);
    document.getElementsByTagName('head')[0].appendChild(s);
    return {
        // true if the library should be dynamically loaded after window.onload.
        injecting: true,
        listener: function(o) {
            // waiting for the loader component
            if (o.name === 'get') {
                window.setTimeout(YAHOO_config.ready, 1);
            }
        },
        ready: function() {
            var loader = new YAHOO.util.YUILoader();
            // defining custom modules
            loader.addModule({
                name: 'bubbling',
                type: 'js',
                fullpath: 'bubbling/bubbling.js',
                requires: ['yahoo', 'event', 'dom']
            });
            loader.addModule({
                name: 'swfobject',
                type: 'js',
                fullpath: 'tools/swf-object.js'
            });
            loader.require(['bubbling', 'swfobject']);
            loader.combine = true;
            loader.insert({
                onSuccess: function() {
                    / initializers and renderers here /
                    YAHOO.util.Get.script([
                        "left-column-renderer.js", "ad-renderer.js"
                    ], {
                        onSuccess: function() {
                            / we are ready to roll, now we can
                               do more initializations here /
                        }
                    });
                    / more initializations here, if
                       you don't want to use Get Utility /
                }
            });
        }
    };
} ();

The developer should define the generic structure for the application within the bootstrap, specifying the requirements, components, and modules for the application. So, basically, the bootstrap would be different for each site, application (or even for page).

Identify what should be loaded using YUI Loader or YUI Get Utility:

The difference between the modules and components/initializers/renderers is that modules are structures based on YUI, with the corresponding registration process, and the components are simple JS files.

All YUI components/utilities/widgets (modules) should be loaded thru the YUI Loader (along with 3rd party modules), and will be loaded on the page once.

YUI Get Utility should load scripts that implement a certain initialization/renderer process for a region in your application, or some kind of delayed routine to enhance the page.

Initialization and renderer processes:

Loader’s “onSuccess” is the first stop in the loading process (“bootstrap ready” in the diagram). At this point we can decide if we want to put all the initialization process within that function, or if we want to load some JavaScript files, and they will carry on with the initialization process for each different region in the page, getting some granularity for each page.

The initialization/renderer process should wait until the DOM structure that represent the region that we want to enhance gets available (onAvailable or onContentReady). If we want to extend/expand that DOM structure, just wait until DOM gets ready (onDOMReady) to avoid malfunctions in IE. Here is an example:

Left Column Renderer (mod-left-column.js):

YAHOO.util.Event.onAvailable('mod-left-column', function (el) {
  / initialization process here /
});

In this case, we should include all the requirements before this block is executed.

In YUI 3.x, things are a lot easier. For example:

YUI({}).use('node', 'json', 'io', function(Y) {
  var node = Y.get('#mod-left-column');
  / initialization process here /
});

Instead of including all the necessary requirements (modules) at the bootstrap level, YUI 3.x Loader will do it automatically during each region’s initialization process

Real examples:

Final notes:

  • The loader object can be used at any time, even during a region initialization part. It’s a good practice in YUI 2.x to have a single reference for the loader. With small changes in the code, we can easily share and re-use the “loader” object thru “YAHOO_config.loader“, across the application.
  • CSS files can be loaded thru YUI Loader or YUI Get Utility in the same way as JavaScript files.
  • For production, it’s a good practice to minimize the number of request (JavaScript files). In this case you can combine all the 3rd party modules in a single file, and even combine all the components/initializers/renderers in a single file.
  • Using inline JavaScript blocks to initialize some of the regions is not a good option; they block the rendering of everything in the page.

More Information:

There are a lot of good articles about on-demand loading and how it helps to improve the performance, as well as its implications. Here are some of them:

More Articles