Customize the Loading Icon

When you're customizing your site, you'll go through the normal things like changing the colors, design, layout, and other things. But there is one ubiquitous thing that you may want to change and not know how: the loading indicator. In fact, you may want to completely change the style of it to make it more... well, you.

By default, we've included a simple animated gif that tracks the mouse position and in this article I'm going to talk about how it works and how you can change the icon itself.

Where It Lives

There are two essential parts of the loading icon:

  1. jQueryExtras > JavaScript > jQuery.ajaxSetup.js
  2. GlobalImages > Images > ajax-loader.gif

The first, jQuery.ajaxSetup.js, is an invocation of the jQuery.ajaxSetup() method. What this method allows us to do is set the defaults for AJAX requests, and we're using it to inject some code that runs every time an AJAX request is made.

The second is the actual icon itself. It's quite simple and standard, so if you wanted to you could simply swap it out / override it with your preferred loading gif. You'll notice that this directory is also where we keep all the other images that are used throughout the site.

jQuery.ajaxSetup.js

There are three major parts of this file that we can look at it. The first is where we're defining what the the loading indicator is; this is the bit wrapped in the jQuery(document).ready function.

The second part deals with capturing the the cursor's position when it moves or the window is resized. We then create functions that control when to show and when to hide the loading indicator.

Finally, we add two event handlers to all AJAX calls: the first on ajaxStart and the second on ajaxStop. In short: show the loading indicator when loading begins and hide it when it ends.

In summary, you see that this file first defines what the loading indicator is (using ajax-loading.gif as the icon), add cursor tracking so that it follows the cursor around, and then, finally, bind it to AJAX events so that it shows when something is loading and hide it when it's finished.

Customize jQuery.ajaxSetup.js

So, as I said earlier, if you like this functionality then you can just replace the loading icon with your own. This is a pretty simple swap-out, just remember to update the icon_height and icon_width variables with your new icon's dimensions.

But what if you want to go in a different direction? For example, some sites like to create an overlay that effectively disables the page until everything has finished loading. From a design point of view, this can look rather attractive and, functionally, has the benefit on preventing the user from performing any action that could interfere with the AJAX call.

However, note that this is a double-edged sword: a non-essential AJAX call could fail and 'block' the user. After all, AJAX is meant to be asynchronous, and doing this effectively makes it behave synchronously — waiting for everything to finish before the user can perform an action.

I'm going to provide you with some code that creates a blocking loading animation, but you don't have to copy me exactly. For example, this method involves a way to 'disable' the page, which you could omit. You could also make your change more subtle; after all, our default solution is already pretty subtle, and is a perfectly adequate method.

So, to start, override jQuery.ajaxSetup.js with the following code:

/*
    © 2015 NetSuite Inc.
    User may not copy, modify, distribute, or re-bundle or otherwise make available this code;
    provided, however, if you are an authorized user with a NetSuite account or log-in, you
    may use this code subject to the terms that govern your access and use.
*/

/*
@module jQueryExtras

#jQuery.ajaxSetup

Changes jQuery's ajax setup defaults for adding the loading icon.
*/

define('jQuery.ajaxSetup', ['jQuery','underscore','Utils'], function (jQuery,_)
{
    'use strict';

    jQuery(document).ready(function ()
    {
        var $body = jQuery(document.body)
        ,     $loading_icon = jQuery('#loadingIndicator');

        if (!$loading_icon.length && !(SC && SC.ENVIRONMENT && SC.ENVIRONMENT.isTouchEnabled))
        {
                // if the icon wasn't there, lets add it and make a reference in the global scope

                var $loading_html = '<div id="loadingIndicator" style="position: fixed; top: 0; left: 0; height: 100%; width: 100%; z-index: 9999;    background-color:rgba(255, 255, 255, 0.85);"><img class="global-loading-indicator" src="' + _.getAbsoluteUrl('img/ajax-loader.gif') + '" style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%)"></div>';

                $loading_icon = jQuery($loading_html).hide();

                if (!_.result(SC, 'isPageGenerator'))
                {
                        $loading_icon.appendTo($body);
                }
        }

        SC.$loadingIndicator = $loading_icon;
    });

    SC.loadingIndicatorShow = function ()
    {
        SC.$loadingIndicator && SC.$loadingIndicator.show();
    };

    SC.loadingIndicatorHide = function ()
    {
        SC.$loadingIndicator && SC.$loadingIndicator.hide();
    };

    // This registers an event listener to any ajax call
    jQuery(document)
        // http://api.jquery.com/ajaxStart/
        .ajaxStart(SC.loadingIndicatorShow)
        // http://api.jquery.com/ajaxStop/
        .ajaxStop(SC.loadingIndicatorHide);

    // http://api.jquery.com/jQuery.ajaxSetup/
    jQuery.ajaxSetup({
        beforeSend: function (jqXhr, options)
        {
            // BTW: "!~" means "== -1"
            if (!~options.contentType.indexOf('charset'))
            {
                // If there's no charset, we set it to UTF-8
                jqXhr.setRequestHeader('Content-Type', options.contentType + '; charset=UTF-8');
            }
        }
    });

});

If you look at a diff of the file after you change it, you'll see that we've removed about 40 lines of code. That's because we've removed the cursor tracking code, so that the loading animation no longer follows the cursor around the page.

In its place we have got some simple HTML. While there was already some HTML being injected into the page, there are some differences:

  • There's a container now. Note the styling on it: it's designed to take up the entire screen and stay there (even if the user scrolls).
  • The loading image's styling has been set to remain perfectly centred within its parent element. Note the use of transform property: this is part of CSS3 and provides an easy solution to the eternal problem of perfectly centering someting (see CSS-Tricks).

That's about it. You can save and deploy your local server to test it out. Rather than performing an AJAX call you can trigger the loading indicator by opening the developer console and running SC.loadingIndicatorShow();. You'll notice that the page is semi-transparent and that you're not able to click on anything behind the container. While the page is loading, therefore, the user is blocked from doing anything.

Above is an image from my site. You'll note I replaced the loading gif with something fancy (I created it using loading.io). The cool thing about this site is that instead of a gif, you can generate CSS and have it generated effectively on-the-fly using animations and keyframes. I think using a static gif is simple and easy, so I'm going to go with that.

Summary

In SCA we include a module that performs some default functions whenever there is an AJAX call on your site; these are contained in jQuery.ajaxSetup.js. Most of these functions relate to an animation show when loading occurs. Showing feedback when something is happening behind the scenes is important for the user, who may want to wait until the page is finished before performing an action.

By default it's set up to show a simple gif that tracks cursor movement and while a simple customization is just to swap out this gif (stored in GlobalImages) you could modify it so that it performs something else. This article provided some code to create a 'blocking' loading animation, which prevents user action while loading is happening. Some people don't like this, as all kinds of AJAX calls cause blocking, and blocking may not be necessary when non-essential calls are being made. However, as long as your JavaScript and services are in good health, this may not be an issue. The result of which can be a relatively pleasant aesthetic effect, and the prevention of interrupted AJAX calls.

Further Reading