15.10 Displaying an Animated Progress Bar
NN 6, IE 5
15.10.1 Problem
You want to show a progress bar while a
long, repetitive script operation takes place.
15.10.2 Solution
Components of this solution consist of a minimum of HTML, a style
sheet to give the HTML its look and feel, and scripts to control the
display and animation of the progress bar. The HTML included in the
page consists of a group of nested div elements,
each of which makes up a separate visible portion of the user
interface: the wrapper around the entire progress bar, the label
text, the empty bar, and the colored bar that expands to the right
during the animation:
<div id="progressBar">
<div id="progressBarMsg">Calculating...</div>
<div id="sliderWrapper">0%
<div id="slider">0%</div>
</div>
</div>
The progress bar is hidden when the page loads, and the rest of its
look and feel is controlled by an extensive style sheet described in
the Discussion. You can customize the appearance by modifying the
style sheet.
Apply the script shown in Example 15-5 to the
progress bar. The sequence used to control the progress bar consists
of calls to the showProgressBar(
), calcProgress( ),
and hideProgressBar( ) functions. See the
Discussion about how to repeatedly invoke calcProgress(
) to convey motion to the bar. The page must also include
onload event handler calls to two initialization
routines: the DHTML API library (Recipe 13.3) and the
initProgressBar(
) function:
<body onload="initDHTMLAPI( ); initProgressBar( )">
15.10.3 Discussion
Despite the simplicity of the HTML for the progress bar, it can
convey a significant amount of information. Figure 15-4 shows what the progress bar looks like in the
middle of its animation.
Other than
the progress message and initial 0% indicators, the look and feel of
the entire bar is controlled by the following
style sheet rules:
<style type="text/css">
#progressBar {position:absolute;
width:400px;
height:35px;
visibility:hidden;
background-color:#99ccff;
padding:20px;
border-width:2px;
border-left-color:#9999ff;
border-top-color:#9999ff;
border-right-color:#666666;
border-bottom-color:#666666;
border-style:solid
}
#progressBarMsg {position:absolute;
left:10px;
top:10px;
font:18px Verdana, Helvetica, sans-serif bold
}
#sliderWrapper {position:absolute;
left:10px;
top:40px;
width:417px;
height:15px;
background-color:#ffffff;
border:1px solid #000000;
text-align:center;
font-size:12px
}
#slider{position:absolute;
left:0px;
top:0px;
width:420px;
height:15px;
clip:rect(0px 0px 15px 0px);
background-color:#666699;
text-align:center;
color:#ffffff;
font-size:12px
}
</style>
Progress bar scripting, as shown in Example 15-5,
relies on the DHTML API from Recipe 13.3 and the
centerOnWindow( ) function from Recipe 13.7.
Example 15-5. Scripts for the progress bar
<script type="text/javascript" src="DHTMLapi.js"></script>
<script type="text/javascript">
// Center a positionable element whose name is passed as
// a parameter in the current window/frame, and show it
function centerOnWindow(elemID) {
// 'obj' is the positionable object
var obj = getRawObject(elemID);
// window scroll factors
var scrollX = 0, scrollY = 0;
if (document.body && typeof document.body.scrollTop != "undefined") {
scrollX += document.body.scrollLeft;
scrollY += document.body.scrollTop;
if (document.body.parentNode &&
typeof document.body.parentNode.scrollTop != "undefined") {
scrollX += document.body.parentNode.scrollLeft;
scrollY += document.body.parentNode.scrollTop
}
} else if (typeof window.pageXOffset != "undefined") {
scrollX += window.pageXOffset;
scrollY += window.pageYOffset;
}
var x = Math.round((getInsideWindowWidth( )/2) - (getObjectWidth(obj)/2)) + scrollX;
var y = Math.round((getInsideWindowHeight( )/2) - (getObjectHeight(obj)/2)) + scrollY;
shiftTo(obj, x, y);
show(obj);
}
// initialize progress bar for IE quirks mode by setting its initial dimensions
function initProgressBar( ) {
// create quirks object whose default (CSS-compatible) values
// are zero; pertinent values for quirks mode filled in later
if (navigator.appName = = "Microsoft Internet Explorer" &&
navigator.userAgent.indexOf("Win") != -1 &&
(typeof document.compatMode = = "undefined" ||
document.compatMode = = "BackCompat")) {
document.getElementById("progressBar").style.height = "81px";
document.getElementById("progressBar").style.width = "444px";
document.getElementById("sliderWrapper").style.fontSize = "xx-small";
document.getElementById("slider").style.fontSize = "xx-small";
document.getElementById("slider").style.height = "13px";
document.getElementById("slider").style.width = "415px";
}
}
// display progress bar
function showProgressBar( ) {
centerOnWindow("progressBar");
}
// update display of bar and percentage completed
function calcProgress(current, total) {
if (current <= total) {
var factor = current/total;
var pct = Math.ceil(factor * 100);
document.getElementById("sliderWrapper").firstChild.nodeValue = pct + "%";
document.getElementById("slider").firstChild.nodeValue = pct + "%";
document.getElementById("slider").style.clip =
"rect(0px " + parseInt(factor * 417) + "px 16px 0px)";
}
}
// hide progress bar
function hideProgressBar( ) {
hide("progressBar");
calcProgress(0, 0);
}
</script>
The centerOnWindow(
) function is called each time the
progress bar is shown (in the showProgressBar( )
function). Thus, if the user resizes the browser window between
displays of the progress bar, the bar is still centered on the
current window.
Due to the heavy use of borders and padding to define the look and
feel of the progress bar, a bit of extra coding is needed to assist
with the sizing of the nested div elements. The
discrepancies in IE between backward- and CSS-compatible modes have a
significant bearing on the various measures. The default
specification from the style sheets is tailored for the
CSS-compatible world, and runs as-is in Netscape 6 or later and IE 5
for the Macintosh. For IE 5, 5.5, and the quirks mode of IE 6, the
initProgressBar( ) function (invoked via the
onload event handler) makes critical adjustments
to several dimensions.
Three functions control the visibility and animation of the progress
bar. Two simply show and hide the bar, but the calcProgress(
) function is the key to the animation.
The bar works by adjusting the clipping rectangle of the
darker-colored, most deeply nested div element. It
is initially set up to be clipped to zero pixel width. Calculations
are based on some current value compared against a maximum
value—notions that can be applied to any quantifiable
operation. The factor (converted to a percentage for display within
the bar space) is applied to the right edge of the clipping
rectangle.
To experiment with the progress bar, I've created
some test functions that simply repeat through a sequence of
calculations with a little bit of irregularity thrown in to make the
display interesting. A global object named
loopObject contains properties to assist with the
experimentation. The runProgressBar(
) function represents the kind of
function you might be using that would benefit from the progress bar
display. The key to successful deployment is a repeating script
triggered by setTimeout( ) or
setInterval( ) so that the page updates between
iterations. Once the target value is achieved, the
calcProgress( ) function is invoked with the same
values as parameters to allow the bar to display the 100% value
briefly, before the progress bar is hidden.
Identifying the kinds of operations to which the progress bar can be
applied may not be easy. Prerequisites include an operation that
takes sufficient time to warrant the display of the progress bar in
the first place. But more importantly, the operations must be
something that occur after the page has loaded so that the progress
bar div elements are already loaded and modified
as needed for quirks-mode versions of IE for Windows.
The operation must also be something for which you have a known
target quantity, so that the current progress can be measured against
that target. One possibility is monitoring the
precaching
of images, provided the operation takes place triggered by the
onload event handler of the page. Your equivalent
of the runProgressBar( ) function should loop
through the array of image objects, checking whether the value of
each object's complete property
is true. If it is, the progress bar is incremented
by one fraction of the image array's length, and
your function should loop again via the setTimeout(
) method to test the next image in the array. If the value
is false, the same index value is passed to the
function again (via the setTimeout( )) to try once
more. Of course, images don't always arrive from the
server in source code order, so the progress bar is likely to be
inconsistent in its progress. Also, if the images are already in the
browser cache, the loop through the array may actually slow down the
user's access to the finished page.
A progress bar is the right solution when the user might get
impatient with something that is truly going on behind the scenes.
Tolerance for delays is greatly extended when you entertain in the
interim.
15.10.4 See Also
Recipe 12.7 for hiding and showing an element; Recipe 13.1 for how to
make an element positionable; Recipe 13.3 for details on the DHTML
API library; Recipe 13.7 for centering a positioned element in the
browser window.
|