Tuesday, August 27, 2013

Progress

Although I've been using Ajax1 for nearly 15 years now, the majority of the projects that I've worked on for the past 5 years have been based almost entirely on Ajax, and I've found that one of the shortcomings of Ajax is the lack of information given back to the user when we use it as a framework. A portion of the responsibility for the lack of information is held by the technology - there are few interfaces between the native user-agent messages and an XMLHttpRequest and a portion of the responsibility for the lack of information is poor web development.

On a related note, one of the things I've been thinking about in the past few weeks is how to build a better progress bar, because I've come to understand that giving the user insight into progress is critical - they need to not only know that something is being done, but how much of that something is done and how much left to do.

If you're familiar with HTML5, you might be asking yourself why I would bother trying to develop a reusable progress bar, considering that we now have a progress2 element. The answer is relatively simple though multifaceted. One reason is that I don't like the overly simplistic style of the default progress element - and even though there are things you can do to change the general style (using vendor prefixes, which I hate) there is relatively little that you can easily do make it look substantially different - making it a circular or animated for example.

Another reason I've bothered, however, is that the progress element doesn't seem to fit what I want it to do. Consider this - by default, an HTML5 progress bar is an 'indeterminate' progress bar with no simple way to force it into some sort of determinate state. It would be nice, for example, if we could set a duration on the progress bar or if we could bind it in some (easy) way to another process.

With all these considerations in mind, I set out to build a better mousetrap. My progress bar uses JavaScript and an HTML5 canvas element so its support is not global, but it does mean it has more support than the progress element.3

Building a library that contains different styles of progress bars is simple. After all, we have at our disposal rectangles, lines, and arcs and the ability to color or fill the shapes we create with flat colors or gradients. By playing with various composite methods and a simple understanding of physics we can even make a simple fuse-style progress bar that burns down while throwing off glowing embers. This meant the whole style issue was almost a no-brainer. Passing in a value for duration and using that and the time passed to calculate the percentage complete makes it easy to force a progress bar that would otherwise be indeterminate to a determinate state - and that easily we've solved the second problem. Making a method of the progress bar that can update the percentage complete4 public makes it easy to bind the progress bar to another process, thus solving the third problem and completing the library.5

Now, for an example that uses something we might actually find in the real world - AJAX. This example will also show the problem with having a single max value.

First, let's create an XMLHttpRequest object with more clearly exposed interfaces. OK, now that we've finished that library5, let's bind the progress event of the library to the progress bar like so

var progress = new Cathmhaol.Fuse({canvas:"pb", color:"#f00", height:200, width:200, borderColor:"#0ff", burnDown:true, radius:75})
, xhr = new Cathmhaol.Xhr();

xhr.events.progress.listen(function(){ progress.setComplete(xhr.complete); });

That's it - that's how I solved all three problems. By the way, if we were using a progress element, we would not have known at the beginning (before the AJAX request was sent) what the max value was, because it would have been the sum of the total bytes for both request and response - and without that total, the progress bar is difficult to update.

Feel free to use the libraries discussed here - I would appreciate if you use them that you keep the author information and contribute back to by identifying bugs or ways you think the performance might be improved.

Notes - links open in a new window

  1. Ajax is a combination of web technologies that, in theory, enable faster, more efficient web applications. You can learn more at http://en.wikipedia.org/wiki/Ajax_(programming)
  2. There are a couple of decent brief introductions to the progress element available, one is at W3 Schools and another is by the HTML5 Doctor, and also talks about how to style the element.
  3. The canvas element is supported in Internet Explorer 9, Firefox, Opera, Chrome, and Safari; however, the progress element is not supported in IE until IE10.
  4. Rather than using the max and value model used by the HTML5 progress element, the library method uses a floating-point value for percentage complete. This gives greater flexibility by not binding the user to a specific max value.
  5. You weren't really expecting to see all of that code here, were you? That library requires a lot of JavaScript, but luckily, you can see all of it in the cathmhaol.com repository at http://js.cathmhaol.com/cjl-fuse.js and http://js.cathmhaol.com/cjl-xhr.js

No comments:

Post a Comment