A Loading Overlay

The Problem

Loading various scripts and other resources and building even a relatively simple UI can take time. As widgets are instantiated and data is fetched, distracting re-rendering of the page can occur. To avoid this, it’s common practice to overlay some or all of your page with a “loading” message that lets your users know what’s going on while simultaneously hiding any re-rendering or intermediate states in your UI that you don’t want them to see. The solution should not cause significant additional load time by requiring too many of its own dependencies, and it should avoid hard-wiring its behavior to any particular event so we can keep some flexibility in how we define “loaded”.

The Solution

Using methods from Dojo Base, we’ll inject an overlay element and use CSS to style and position it. A simple API will be developed to easily switch in and out of the loading state, and some simple animation will be used to provide a smooth transition between states. This will be accomplished using dojo.create, dojo.fadeOut, dojo.style, and dojo.ready.

Here’s what we will be building:

Discussion

One of the challenges here is not the implementation itself, but juggling the priority of various requirements.
One could imagine creating a complicated loader widget with a percentage bar, maybe even a non-modal or minimized state which allows the user to continue interacting with the rest of the page while other components load in the background.
This not that recipe; we wanted to keep this implementation lightweight and strike a balance between demonstrating practical techniques for developing with Dojo, while keeping it simple enough to solve the most common case using a minimum of resources.

Step 1: Building the Overlay

If you use your DOM inspector of choice on the demo page, you’ll see the loading overlay appears as a child element of the <body>. It’s just a plain <div>, and its size and positioning are handled by CSS.

We’re creating the overlay element in JavaScript so that it doesn’t need to be hardcoded into every page we want to apply this behavior to. Since all of the ingredients to do this are included in Dojo Base, we don’t even necessarily need to wait for dojo.ready—as long as our code runs after dojo.js has been included on the page, and after the <body> element is created (but not necessarily closed/complete), we can add elements to the DOM at will.

Because we’re injecting the overlay element using Dojo, if Dojo doesn’t load for some reason, or the user has JavaScript disabled, the overlay simply won’t be created. That’s good, because it means the site can still be used in those scenarios. The alternative approach of having the overlay element already in the page and visible on load means that, unless everything is working perfectly, the overlay remains enabled forever with no way for the user to dismiss it.

The Dijit themes each come with their own images directory, complete with icons, gradients, and other handy graphical assets used by the Dijit widgets. Even if you don’t use Dijit on your page, all these graphics are licensed under the same terms as the code and are ready for use.

A module structure

There are three things we will be wanting from this overlay:

Creation and set-up of the overlay element(s)

Show the overlay to indicate loading in progress

Hide the overlay to indicate loading complete

We'll turn this into three methods on an overlay object: init, show and hide.

To make management of private properties and state easier; and to avoid creating unnecessary globals, we’ll write a function that returns our loadingOverlay object.
The approach we'll use is sometimes known as the "module pattern" and looks like this:

Our CSS has already positioned the overlay, so all we need to do in JavaScript is toggle the display style property to make it show up. While there are many different methods for doing this, using dojo.style helps prevent accidental specificity conflicts (versus adding/removing a class) and ensures any cross-browser behavior is normalized (versus setting style properties directly on the element).

We can now test this from the console. Load up the page, and in your browser’s console type: loadingOverlay.hide():

dojo.fadeOut is part of Dojo Base, so is available and ready to use immediately. However, all it does is animates the opacity of the node; we need to add an additional step at the end of the animation to set display: none. Without this extra step, the overlay would still be present but invisible and would block clicks on the page.

Remember that animations happen asynchronously, so any statements after dojo.fadeOut will run before the animation completes. Review the effects and animation tutorials for more information on using animation.

Step 4: Defining “Loaded”

With the implementation of our API essentially complete, our last step is to choose when to call each function. You’re probably familiar by now with dojo.ready, so let’s hook that up:

// put up the loading overlay while the page initializes
loadingOverlay.init().show();
dojo.ready(function(){
// take down the loading overlay when the page is ready
loadingOverlay.hide();
});

That works, and perhaps it is enough. As you add code dependencies to your page with dojo.require, the overlay will stay up until they are all loaded. However, since we created an API for this overlay, we’ve got much more flexibility in how we define “loaded”. We can call loadingOverlay.hide() whenever we want and with its partner function loadingOverlay.show(), we can re-use this overlay later for other load-related events (such as Ajax requests).

To explore this, let's add some more context and see how it plays out with a page with multiple load events.
Our plain demo page now makes a Twitter search API call to fetch a list of recent mentions of a given keyword.
There's an initial "loaded" event when the page, its dependencies and the initial query are loaded.
By clicking a button to make a new query, the page goes back into a loading state and we need to toggle the overlay again.

There's a new demo object with requestData and renderItems methods. These are responsible for getting the query results from Twitter into the page. The piece of interest for this tutorial is the integration of the loading overlay:

The demo.requestData function returns a Deferred object; it lets us add callbacks that will be executed when the request is complete. (You can find out more about this technique in the Getting Started with Deferreds tutorial.) Up to this point, the loading overlay is still visible, so instead of simply adding the code to hide it in a dojo.ready block, we can hide it here in the requestData callback.

The dojo.query statement sets up a click handler on each of the buttons to issue a new Twitter query. We put up the loading overlay at the start of this operation and take it down in the requestData callback.

Polish and refinements

Although the demo is functional, to really tighten up there are a few refinements needed. If you clicked the buttons fast enough, the requests stack up and we can end up with overlapping fadeOut animations and even rendering the same results multiple times.
To make it reentrant we can add guards to handle both these problems:

Conclusion

Judicious application of just a few Dojo methods has created a nicer user experience and put you the developer in control of what you load when, and what your user sees in the meantime.
We've applied the module pattern with Dojo to contain and manage the solution; selectively exposing methods and properties for use on the page. This recipe's solution is of course just one way to tackle the stated problem, but even with the refinements with the Twitter demo integration, it remains simple, lightweight and readily extended in any direction your requirements take you.