Tuesday, November 23, 2010

jQuery UI Overall Progress Bar

The jQuery UI Javascript user interface library has a progress bar widget used to indicate the progress of something.  That something can be anything, something happening locally in the browser or some server process.  Sometimes it is useful to show the overall progress of several smaller, but related tasks.  It would be nice if we could automate this with a specialized progress bar widget.

Thankfully, we can specialize the progress bar widget for this purpose.  We can create a progress bar that observes a set of other progress bars to measure and display the overall progress.  Here is an example of how we might go about doing this.

Here is the HTML markup:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>

        <title>Custom Progress Bar</title>

        <link href="jqueryui/css/redmond/jquery-ui-1.8.5.custom.css" rel="stylesheet" type="text/css" />
        <script type="text/javascript" src="jqueryui/js/jquery-1.4.2.min.js"></script>
        <script type="text/javascript" src="jqueryui/js/jquery-ui-1.8.5.custom.min.js"></script>
        <script type="text/javascript" src="js/overallprogress.js"></script>
        <script type="text/javascript" src="js/demo.js"></script>
        <script type="text/javascript">

            $(document).ready(function(){
            
                $("#sp1, #sp2, #sp3").progressbar();
                $("#ov").overallprogress( {subject: $("#sp1, #sp2, #sp3")} );
                               
                timer();
            
            });
        
        </script>
        <style type="text/css">
        
            .progress {
            
                width: 25%;
            
            }
            
            .container {
            
                padding: 10px;
            
            }
        
        </style>

    </head>
    
    <body>
        
        <h1 class="ui-widget ui-widget-header">Custom Progress Bar</h1>
        
        <div class="container ui-widget ui-widget-content">
            
            <p>Server Process 1</p>
            <div id="sp1" class="progress"></div>
            
            <p>Server Process 2</p>
            <div id="sp2" class="progress"></div>
            
            <p>Server Process 3</p>
            <div id="sp3" class="progress"></div>
            
            <p>Overall Progress</p>
            <div id="ov" class="progress"></div>
            
        </div>
        
    
    </body>
    
</html>

Here we create four progress bar widgets.  The first three are server processes that we want to show the progress of.  The fourth progress bar uses a custom progress bar widget that we'll show below.

Here is the demo Javascript, demo.js, that will simulate the server processes:


function timer(){

    var v1 = $("#sp1").progressbar("value");
    var v2 = $("#sp2").progressbar("value");
    var v3 = $("#sp3").progressbar("value");
    
    v1 += 5;
    v2 += 10;
    v3 += 5;
    
    if (v1 > 100) {
    
        v1 = 100;
    
    }
    
    if (v2 > 100) {
    
        v2 = 100;
        
    }
    
    if (v3 > 100) {
    
        v3 = 100;
        
    }    
    
    if ( (v1 + v2 + v3) < 300){
    
        setTimeout("timer()", 1000);
    
    }
    
    $("#sp1").progressbar("value", v1);
    $("#sp2").progressbar("value", v2);
    $("#sp3").progressbar("value", v3);
    
}

Here, we set a timer that will update the three server precesses.  The progress bar widgets are in turn updated.

Finally, we have overallprogress.js, our custom progress bar widget:

$.widget("ui.overallprogress", $.ui.progressbar, {

    options: {
        
        subject: false
    
    },
    
 _init: function () {
 
     $.ui.progressbar.prototype._init.call(this);

     var subject = this.options.subject;
     var overall = this;
        
  
  if (subject) {
  
      subject.bind("progressbarchange", function(event, ui) {
      
          var total = 0;
          
          $.each(subject, function(){
          
              total += $(this).progressbar("value");
          
          });
          
          overall.value(total/subject.size());
          
      });
  
  }

 },


});

The overallprogress widget takes a subject parameter when it is created.  The subject is observed by the overall progress widget.  In our example, it is passed a jQuery object containing the three regular progress bar widgets.  It then listens for progressbarchange events, on which, it updates the overall progress.

The reason we defined a new widget for this purpose is that it is generic enough to be useful in other contexts.  With custom widgets, we can encapsulate a lot of the common stuff that can be reused.  Here is what overallprogress looks like in action: