Quick Tip: Add a Progress Bar to Your Site

Demo Download

Since the advent of mobile, web sites are turning more and more into "apps". Concepts that make sense for a locally running application are transferred to the web. One of these is the recent addition of "progress bars" to some of Google's websites that show the loading state of the page.

In this quick tip, we will use the new NProgress jQuery plugin to add a progress bar to a web page. If you'd like to learn more, keep reading!

The NProgress Plugin

NProgress is a jQuery plugin that shows an interactive progress bar on the top of your page, inspired by the one on YouTube. It consists of a global object - NProgress which holds a number of methods that you can call to advance the progress bar. Here is a quick demo of the methods:

$(function(){

    // Quick Load

    $('button.quick-load').click(function(){
        NProgress.done(true);
    });

    // Incremental Load

    $('button.show-progress-bar').click(function(){
        NProgress.start();
    });

    $('button.load-one-item').click(function(){
        NProgress.inc();
    });

    $('button.finish').click(function(){
        NProgress.done();
    });

    // Percentage Load

    $('button.set-to-25').click(function(){
        NProgress.set(0.25);
    });

    $('button.set-to-75').click(function(){
        NProgress.set(0.75);
    });

});
<div>
    <h1>Quick Load</h1>
    <p>Show the progress bar quickly. This is useful for one-off tasks like AJAX requests and page loads.</p>
    <button class="quick-load">Quick Load</button>
</div>

<div>
    <h1>Incremental Load</h1>
    <p>The progress bar is incremented with every element that is loaded. This can be useful in web apps that load multiple items.</p>
    <button class="show-progress-bar">Show Progress Bar</button>
    <button class="load-one-item">Load An Item</button>
    <button class="finish">Finish Loading</button>
</div>

<div>
    <h1>Percentage Load</h1>
    <p>NProgress lets you set the progress bar to a specific percentage. This can be useful in apps where you know the total number of the items to be loaded, so you can calculate the percentage. This is the technique that we will use in the demo.</p>
    <button class="show-progress-bar">Show Progress Bar</button>
    <button class="set-to-25">Set to 25% Loaded</button>
    <button class="set-to-75">Set to 75% Loaded</button>
    <button class="finish">Finish Loading</button>
</div>
*{
    margin:0;
    padding:0;
}

body{
    font:14px/1.3 'PT Sans', sans-serif;
    color: #5e5b64;
    padding:40px 40px 0;
}

h1{
    font-size:18px;
    padding-bottom:4px;
}

button{

    background-color: #78bad6;
    box-shadow: 0 0 5px #8fcde7 inset, 0 1px 1px #eee;

    display: inline-block;
    padding: 9px 15px;
    margin: 20px auto 20px;

    font-weight: bold;
    font-size: 12px;
    text-align: center;
    color: #fff;

    border-radius: 2px;
    box-shadow: 0 1px 1px #e0e0e0;
    border: 0;

    opacity:1;
    cursor: pointer;
}

button:hover{
    opacity: 0.9;
}

The plugin github page suggests that you hook up the NProgress.start() function to your $(document).ready() callback and NProgress.done() to $(window).load() which is a very easy way to integrate the plugin. This won't show the real progress (for that you will have to monitor all the resources that are included in your page and increment the bar manually), however most people won't notice anyway.

Now that you have a good idea of how NProgress is used, let's make a more complicated example - a gallery that shows a progress bar while loading images. The bar will correspond to the actual number of images loaded.

The Gallery

As usual, we start off with the HTML markup. This time it is very simple, we only have need a div to hold the photos, and a load button:

index.html

<!DOCTYPE html>
<html>

    <head>
        <meta charset="utf-8"/>
        <title>Quick Tip: Add a Progress Bar to Your Site</title>

        <link href="http://fonts.googleapis.com/css?family=PT+Sans+Narrow:700" rel="stylesheet" />

        <!-- The Stylesheets -->
        <link href="assets/nprogress/nprogress.css" rel="stylesheet" />
        <link href="assets/css/style.css" rel="stylesheet" />

    </head>

    <body>

        <h1>Gallery Progress Bar</h1>

        <div id="main"></div>

        <a href="#" id="loadMore">Load More</a>

        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
        <script src="assets/nprogress/nprogress.js"></script>
        <script src="assets/js/script.js"></script>

    </body>
</html>

I am including a custom font from Google Webfonts and two stylesheets in the <head>, and three JavaScript files before the closing </body> tag.

progress-bar.jpg
Progress Bar

Things get more interesting in the jQuery part of the tutorial. Here I am using the Deferred object to show the photos consecutively. This is needed, because we want the photos to download in parallel (which is much faster), but fade into view one after the other. This article is too short to explain how Deferreds work, but you can read through one of these: link, link, link. They are a powerful tool that can simplify asynchronous interactions.

assets/js/script.js

(function($){

    // An array with photos to show on the page. Instead of hard 
    // coding it, you can fetch this array from your server with AJAX.

    var photos = [
        'assets/photos/1.jpg',  'assets/photos/2.jpg',
        'assets/photos/3.jpg',  'assets/photos/4.jpg',
        // more photos here
    ];

    $(document).ready(function(){       

        // Define some variables

        var page = 0,
            loaded = 0,
            perpage = 10,
            main = $('#main'),
            expected = perpage,
            loadMore = $('#loadMore');

        // Listen for the image-loaded custom event

        main.on('image-loaded', function(){

            // When such an event occurs, advance the progress bar

            loaded++;

            // NProgress.set takes a number between 0 and 1
            NProgress.set(loaded/expected);

            if(page*perpage >= photos.length){

                // If there are no more photos to show,
                // remove the load button from the page

                loadMore.remove();
            }
        });

        // When the load button is clicked, show 10 more images 
        // (controlled by the perpage variable)

        loadMore.click(function(e){

            e.preventDefault();

            loaded = 0;
            expected = 0;

            // We will pass a resolved deferred to the first image,
            // so that it is shown immediately.
            var deferred = $.Deferred().resolve();

            // Get a slice of the photos array, and show the photos. Depending
            // on the size of the array, there may be less than perpage photos shown

            $.each(photos.slice(page*perpage, page*perpage + perpage), function(){

                // Pass the deferred returned by each invocation of showImage to 
                // the next. This will make the images load one after the other:

                deferred = main.showImage(this, deferred);

                expected++;
            });

            // Start the progress bar animation
            NProgress.start();

            page++;
        });

        loadMore.click();
    });

    // Create a new jQuery plugin, which displays the image in the current element after
    // it has been loaded. The plugin takes two arguments:
    //  * src - the URL of an image
    //  * deferred - a jQuery deferred object, created by the previous call to showImage
    // 
    // Returns a new deferred object that is resolved when the image is loaded.

    $.fn.showImage = function(src, deferred){

        var elem = $(this);

        // The deferred that this function will return

        var result = $.Deferred();

        // Create the photo div, which will host the image

        var holder = $('<div class="photo" />').appendTo(elem);

        // Load the image in memory

        var img = $('<img>');

        img.load(function(){

            // The photo has been loaded! Use the .always() method of the deferred
            // to get notified when the previous image has been loaded. When this happens,
            // show the current one.

            deferred.always(function(){

                // Trigger a custom event on the #main div:
                elem.trigger('image-loaded');

                // Append the image to the page and reveal it with an animation

                img.hide().appendTo(holder).delay(100).fadeIn('fast', function(){

                    // Resolve the returned deferred. This will notifiy
                    // the next photo on the page and call its .always() callback

                    result.resolve()
                });
            });

        });

        img.attr('src', src);

        // Return the deferred (it has not been resolved at this point)
        return result;
    } 

})(jQuery);

The progress bar is incremented with every loaded image by the callback function that listens for the image-loaded custom event. This way the showImage function is free to handle only the loading and displaying of the photos.

With this the gallery progress bar is ready!

Bootstrap Studio

The revolutionary web design tool for creating responsive websites and apps.

Learn more

Related Articles

Max Requiao

Martin Angelov just need to be responsive, but great ta

Daniel Steiner

I guess making THAT Project responsive shouldn't be a real problem ;)

Fantastic!

Kenul Jain

The Loading is just applicable to the images
what if i want to apply the loading to my whole website

Martin Angelov

One way is to hook up the NProgress.start() function to your $(document).ready() callback and NProgress.done() to $(window).load(). This will show the progress bar while the page loads. It won't show the real progress - you can't get that with JS, but it will at least give people an indication that something is happening.

Thanks Martin. Can you show us how to integrate nprogress to history.js or pjax. Whole website for example and when you click the menu there is no reloading but the url is updated and ofcourse with a progress bar at the top.

Thanks your my hero

Vyacheslav

How come you put img.attr('src', src); outside the img.load({...}) scope and not inside?

Martin Angelov

The load event is triggered when the image finishes loading. This happens after you set the src attribute to point to a image url.

Zack Bloom

You might want to take a look at Pace as well, it's a little easier to integrate.

Luis Franco

Thanks Zack, Pace is a great time saver.

Progress bar "loading" circle is not working in IE8.

Ohio Web Design

I really like the NProgress plugin. Is anyone aware if the kitkat.com progress bar is custom built?

Ian Maybach

Can you please explain the "image-loaded" custom event? Where to find it?