Video icon 64
Learn to Code. Start your free trial today.
Advertisement

Create a Progress Bar With Javascript

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →

The Progress Bar is one of the latest components to be added to the excellent library of UI widgets and interaction helpers built on top of jQuery. It was introduced in the latest version of the library, which at the time of writing is 1.7.

The progress bar is currently only determinate, which means when we update it, we have to tell it explicitly what its value is, and we must know beforehand when the process it is used to measure completes. This widget is not currently the best choice for a process which will take an indeterminate length of time to complete.
It’s a very simple widget with a small API that exposes a limited number of properties and methods, but it can still be highly effective and is great for providing visual feedback to visitors on the percentage of a process is left before it is complete.

Getting Started

We’ll need a copy of the current version of jQuery UI, which can be obtained from the download builder at http://jqueryui.com/download. Once we’ve downloaded it, we’ll need to unpack it so that the existing directory structure is preserved. We should create a new directory on our computer called jQuery UI and then inside this create another new folder called jqueryui1.7. The archive should then be unpacked to the jqueryui1.7 folder.

The archive will contain everything we need to get started; minified and uncompressed versions of all of the library files, some theme files (the default theme is the aptly-named smoothness), and even the latest version of the underlying jQuery library.

The Progress bar relies on a number of files in order to function; these are listed below in the order in which they should be added to our page:

  • ui.core.css
  • ui.theme.css
  • ui.progressbar.css
  • jquery[currentversion].js
  • ui.core.js
  • ui.progressbar.js

The first three files are part of the extensive CSS framework and are used to give the progress bar its distinct appearance. We don’t need to stick with this theme in a real-world implementation; we have many options for customization including a huge number of pre-configured themes available directly from Themeroller, a custom theme we can design ourselves using Themeroller, or even a custom theme we create manually by overriding rules defined in the default stylesheets. We won’t be doing any of those things in this tutorial however, but we can make use of some of the classes provided by the framework.

The Underlying Page

Very little underlying mark-up is needed by this widget; all we need, in addition to the library resources listed above, is a simple container element. In your text editor, create the following page shell with the required resources and container element:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="jqueryui1.7/development-bundle/themes/smoothness/ui.core.css">
    <link rel="stylesheet" type="text/css" href="jqueryui1.7/development-bundle/themes/smoothness/ui.theme.css">
    <link rel="stylesheet" type="text/css" href="jqueryui1.7/development-bundle/themes/smoothness/ui.progressbar.css">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>jQuery UI Progress Bar</title>
  </head>
  <body>
    <div id="container"></div>
    <script type="text/javascript" src="jqueryui1.7/development-bundle/jquery-1.3.2.js"></script>
    <script type="text/javascript" src="jqueryui1.7/development-bundle/ui/ui.core.js"></script>
    <script type="text/javascript" src="jqueryui1.7/development-bundle/ui/ui.progressbar.js"></script>
    <script type="text/javascript">
	
    </script>
  </body>
</html>

Save this as progressBar.html in the root jQuery UI directory. We put the stylesheets right at the start of the file and the scripts right at the end; this is for performance reasons as pages load the content quicker when they aren’t trying to load JavaScript at the same time. This is a well documented performance practice that is best adhered to. We’ve left an empty script tag at the bottom of the page; let’s add some code there next:

$(function() {

  //call progress bar constructor			
  $("#container").progressbar();
});

In order to initialize the default progress bar, all we do is call its constructor method, progressbar, on the container element that the widget is to be rendered into. When you run this page in your browser, you should see that the progress bar has been created and automatically fills the width of its container, which in this case is the body of the page:

Setting the Value of the progress bar

The progress bar’s value will be set to zero by default, which is why it appears empty in the previous screenshot. To fill the progress bar, we need to set the value property; change the constructor function so that it appears as follows:

//call progress bar constructor
$("#container").progressbar({ value: 50 });

The value property determines the percentage of the progress bar that is filled, giving great visual feedback to the visitor on how much of the task is left to complete. The progress bar should now be half filled, as in the following screenshot:

Getting the Value of the Progress Bar

Getting the current value of the widget is as easy as it was to set it; we can use one of its methods to return the current value property. After the initial constructor, add the following code:
//set mouseover for progress bar

$("#container").mouseover(function() {
				
  //display the current value
  $("<p>").attr("id", "percentage").text($("#container").progressbar("option", "value") + "% complete").appendTo("body");					
});
				
//set mouseout for progress bar
$("#container").mouseout(function() {
				  
  //hide value
  $("#percentage").remove();
});

We’ve added two simple anonymous functions which are triggered on the mouseover and mouseout events fired by the progress bar (note that these are standard DOM events not custom progress bar events). All we do in the first function is create a new paragraph with the current value of the progress bar as its innerText and append it to the page.

The value is retrieved using the option method. The argument passed to the method is the name of the property we’d like to retrieve. The second function simply removes the message again. The message is shown in the following screenshot:

Properties, Events and Methods

The value property, or option, is currently the only configurable property of the progress bar; in this example we set it when the widget is initialized by passing it in as the property of a configuration object. To set this property after the widget has been initialized we would use the option method. To use this method in setter mode, we need to pass in a second parameter specifying the new value, like this:

progressbar("option", "value", 75)

You may be wondering why I said ‘a second parameter’ when clearly there are three arguments in the above line of code. Even though we’re using the option method, we’re not actually calling it directly. Instead we’re calling the constructor method again, but telling it that we’d like to call the option method. The widget will call the method internally, passing in the two parameters (“value” and 75) that we pass to the constructor after the method name.

The progress bar exposes a single event, the change event, which provides a mechanism for us to hook into so that we can respond to changes in its value. This is a custom event so we can detect and react to it in two different ways. We can define an anonymous function as the value of the change property in a configuration object, like we did with the value property, or we can use jQuery’s bind method to specify the anonymous function to execute. A subtle difference between the two is that the code specified using the bind method will be executed first.
The progress bar API exposes five methods, which are listed below:

  • destroy
  • disable
  • enable
  • option
  • value

All of these methods are used in exactly the same way as the option method that we’ve looked at; by calling the constructor method specifying the name of the method and any parameters that we’d like to pass in. Most of these should be pretty self-explanatory.

Using the Progress Bar

This example so far has been very basic, for those of you that may not have used jQuery UI at all before. Let’s step things up a little and put together something approaching the sort of thing we might like to do in a proper implementation. This example will be basic as well, but it should give a much better idea of how the widget can be made to work for us. Our finished page will appear something like this:

Make a start with the following underlying page in a new file in your text editor:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="jqueryui1.7/development-bundle/themes/smoothness/ui.core.css">
    <link rel="stylesheet" type="text/css" href="jqueryui1.7/development-bundle/themes/smoothness/ui.theme.css">
    <link rel="stylesheet" type="text/css" href="jqueryui1.7/development-bundle/themes/smoothness/ui.progressbar.css">
    <link rel="stylesheet" type="text/css" href="regForm.css">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>jQuery UI Progress Bar</title>
  </head>
  <body>
    <div class="form-container ui-helper-clearfix ui-corner-all">
      <h1>Registration Form</h1>
      <p>Progress:</p>
      <div id="progress"></div><label id="amount">0%</label>
        <form action="serverScript.php">
          <div id="panel1" class="form-panel">
            <h2>Personal Details</h2>
              <fieldset class="ui-corner-all">
	<label>Name:</label><input type="text">
	<label>D.O.B:</label><input type="text">
	<label>Choose password:</label><input type="password">
	<label>Confirm password:</label><input type="password">
              </fieldset>
            </div>
            <div id="panel2" class="form-panel ui-helper-hidden">
              <h2>Contact Details</h2>
              <fieldset class="ui-corner-all">
                <label>Email:</label><input type="text">
	<label>Telephone:</label><input type="text">
	<label>Address:</label><textarea rows="3" cols="25"></textarea>
              </fieldset>
            </div>
            <div id="thanks" class="form-panel ui-helper-hidden">
              <h2>Registration Complete</h2>
              <fieldset class="ui-corner-all">
  	<p>Thanks for registering!</p>
              </fieldset>
            </div>
            <button id="next">Next ></button><button id="back" disabled="disabled">< Back</button>
          </form>
        </div>
        <script type="text/javascript" src="jqueryui1.7/development-bundle/jquery-1.3.2.js"></script>
        <script type="text/javascript" src="jqueryui1.7/development-bundle/ui/ui.core.js"></script>
        <script type="text/javascript" src="jqueryui1.7/development-bundle/ui/ui.progressbar.js"></script>
        <script type="text/javascript">

Save this as regForm.html in the jQuery UI folder. At the top of the page we link to the CSS framework; this is primarily to add the required styling for the progress bar, but we can also make use of some of the classes it provides on our own elements. We also add a custom stylesheet that we’ll create soon.

The body of the page contains a few layout elements and some text nodes, but the main elements are the container for the progress bar and the form. The form is separated into several different sections using div and fieldset elements. The reason for this is so that we can hide part of the form to make it appear as if it spans several pages.

We’ve added a paragraph and a label next to the progress bar, we’ll position these so that they appear inside the progress bar. The paragraph contains a simple text string. The label will be used to show the current progress value.

The outer container is given several class names; the first is so that we can apply some custom styling to the element, but the second two are to target different features of the CSS framework. The ui-helper-clearfix class is used to automatically clear floated elements and is a great way of reducing the clutter of additional and unnecessary div elements.

The ui-corner-all class is used to give the container element (as well as the progress bar itself which has them automatically, and our fieldset elements) rounded corners using several proprietary style rules. These are only supported by gecko and webkit based browsers, but in the nature of progressive enhancement it is perfectly acceptable to use them. Progressive enhancement dictates that we can provide enhanced styling on our web pages for browsers that are capable of displaying it. Other browsers will just have a square-cornered container.

We use another class from the CSS framework within the form; several panels need to be hidden when the page first loads, we can therefore make use of the ui-helper-hidden class to ensure that they are set to display:none, when we want to show them, all we have to do is remove this class name.

At the bottom of the body (for performance reasons; this really does work by the way!) we link to the required JavaScript resources from the library. The very last script element is empty and waiting for the code that will bring the form and the progress bar to life. Let’s add that next:

$(function() {

  //call progress bar constructor
  $("#progress").progressbar({ change: function() {

    //update amount label when value changes
    $("#amount").text($("#progress").progressbar("option", "value") + "%");
  } });

  //set click handler for next button
  $("#next").click(function(e) {

  //stop form submission
  e.preventDefault();

  //look at each panel
  $(".form-panel").each(function() {

    //if it's not the first panel enable the back button
    ($(this).attr("id") != "panel1") ? null : $("#back").attr("disabled", "");

	//if the panel is visible fade it out
	($(this).hasClass("ui-helper-hidden")) ? null : $(this).fadeOut("fast", function() {

	  //add hidden class and show the next panel
	  $(this).addClass("ui-helper-hidden").next().fadeIn("fast", function() {

	    //if it's the last panel disable the next button
    	    ($(this).attr("id") != "thanks") ? null : $("#next").attr("disabled", "disabled");
								
	    //remove hidden class from new panel
	    $(this).removeClass("ui-helper-hidden");
								
	    //update progress bar
	    $("#progress").progressbar("option", "value", $("#progress").progressbar("option", "value") + 50);
	  });
      });
    });
  });

});

Within the outer document.ready short-cut we have the constructor function for the progress bar; we pass the constructor a literal configuration object containing a single property. This is the change property and allows us to supply an anonymous function to execute each time the custom change event is detected. We can use this event to update the label that we’re going to position inside the progress bar.

Each time the event is fired we’ll grab the current value of the progress bar using the option method and set the value as the text of the label. The event is fired after the change takes place, so the value we obtain will always be the new value.

Next we have a click handler for the next> button; when this button is clicked it will result in the current ‘page’ of the form changing, via a series of animations, and the value of the progress bar updating. We also need to do a few other things. The default behavior of a button inside a form is to submit the form, which we don’t want to do at this stage so the first thing our click handler does is prevent the form being submitted using the preventDefault() JavaScript function. This is called on the event object, which is automatically passed to the anonymous function.

We then look through each of the separate panels in the form to determine the current panel; the first thing we do is check that the current panel is not the first panel and if it isn’t, we enable the back button which is disabled by default. Only one panel will be displayed at one time, so we find the panel that doesn’t have the ui-helper-hidden class and fade it out. We specify an anonymous callback function to execute once the fade finishes.

Within this second function we select the next element and show it; if the next element is the final panel, which has an id of thanks, we disable the next> button. Although we don’t worry about actual submission of the form in this example, this is where we could send the data gathered from the form to the server. We remove the ui-helper-hidden class as the panel is now visible.

Finally we use the option method once again, this time in setter mode, to set the new value of the progress bar. The new value, that we pass to the method as the second parameter is simply the current value plus 50, as there are just 2 parts of the form. This last part will then trigger the function which updates the label.

Next we need to add a very similar click handler for the

//set click handler for back button
$("#back").click(function(e) {
				  
  //stop form submission
  e.preventDefault();
					
  //look at each panel
  $(".form-panel").each(function() {
					  					
    //if it's not the last panel enable the next button
    ($(this).attr("id") != "thanks") ? null : $("#next").attr("disabled", "");
					  
    //if the panel is visible fade it out
    ($(this).hasClass("ui-helper-hidden")) ? null : $(this).fadeOut("fast", function() {
						  
      //add hidden class and show the next panel
      $(this).addClass("ui-helper-hidden").prev().fadeIn("fast", function() {
							
        //if it's the first panel disable the back button
    	  ($(this).attr("id") != "panel1") ? null : $("#back").attr("disabled", "disabled");
										
	  //remove hidden class from new panel
	  $(this).removeClass("ui-helper-hidden");
								
	  //update progress bar
	  $("#progress").progressbar("option", "value", $("#progress").progressbar("option", "value") - 50);
      });
    });
  });
});

This is now all of the code that we’ll need, all we have to do now is add some basic CSS to lay the example out; in a new file in your text editor add the following code:

h1, h2 { font-family:Georgia; font-size:140%; margin-top:0; }
h2 { font-size:100%; margin:20px 0 10px; text-align:left; }
.form-container {
  width:400px; margin:0 auto; position:relative; font-family:Verdana;
  font-size:80%; padding:20px; background-color:#e0e3e2;
  border:3px solid #abadac;
}
.form-panel { width:400px; height:241px; }
.form-panel fieldset {
  width:397px; height:170px; margin:0 auto; padding:22px 0 0;
  border:1px solid #abadac; background-color:#ffffff;
}
.form-panel label {
  width:146px; display:block; float:left; text-align:right;
  padding-top:2px; margin-right:10px;
}
.form-panel input, .form-panel textarea {
  float:left; width:200px; margin-bottom:13px;
}
.form-container button { float:right; }
p {
  margin:0; font-size:75%; position:absolute; left:30px; top:60px;
  font-weight:bold;
}
#amount {
  position:absolute; right:30px; top:60px; font-size:80%;
  font-weight:bold;
}
#thanks { text-align:center; }
#thanks p {
  margin-top:48px; font-size:160%; position:relative; left:0; top:0;
}

Save this as regForm.css in the same folder as the HTML file. We should now have a working page with a wired up progress bar. When we run the page we should find that we can navigate through each panel of the form, and the progress bar will update itself accordingly:

Summary

In this article we looked at the extremely useful progress bar, which we can use as a visual aid to tell visitors how much longer a specified process is going to take to complete percentage-wise. It provides an attractive and useful presentation of the information, in a way that should be easily understood by our visitors.

We looked at the property and event that are exposed by its API and we looked at one of the methods that we can call to make the progress bar do something. The example that we ended with should run perfectly in all the major browsers, although it does look a little messy in good ol’ IE (which would be very easy to fix were we inclined to do so).

  • Subscribe to the NETTUTS RSS Feed for more daily web development tuts and articles.


Advertisement