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="http://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.
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!