Somewhere in between presenting static information graphics and complex, interactive data dashboards there's a need for a way to visualize moderately dynamic data on the web. Oftentimes the solutions you see implemented are clunky, for example, manually creating multiple frames of various data points and uploading them by hand. These methods scale poorly and try even the most patient.
Traditionally the task of combatting the challenge has fallen to Flash. To be sure, it is a fine choice. I am not here to rail against Flash and contend that absolutely all things on the web should be done with HTML, CSS, and JavaScript. Flash is tailor-made for creating dynamic web experiences. However, using Flash in some cases might be the technological equivalent of hitting a fly with a sledgehammer.
By using a standardized set of web technologies, it is possible to produce interesting data visualizations that degrade gracefully in the face of browser inferiority. Through careful semantic crafting of our XHTML containers, clever use of CSS, and a splash JavaScript, we can build data visualizations that enhance our message and communicate better to our users. As a frame for this technique, I have created a fake site for a university fundraising event. The data visualization we're going to be working with here is a temperature gauge that will display progress towards a monetary goal. Go ahead and take a sneak peek at the final result.
Building the XHTML

As with most techniques, the foundational XHTML is the most important piece, enabling us to progressively enhance the visual presentation. The structure of our XHTML will be in three parts:
  1. The container of the data visualization
  2. The progress indicator to be animated
  3. A textual representation of the data
The structure for this is alarmingly simple, so I won’t drag it out over multiple steps for you.

It may seem as though we have an extra div element here. In many other CSS articles, authors go to great lengths to reduce the number of div elements needed to construct a page, oftentimes relying on readily-available block-level elements. While this methodology has great merit, I will always use an extra div instead of using a p when it comes to the visual composition of a key page element. More often than not my work is handed off to a different design/content team and I have no idea if they are going to realize the p tag has a purpose beyond simple content formatting. Using an extra div here protects the integrity of the visual structure moving forward.
Jiggering the CSS

The visualization component will be handled through a clever combination of background images and property manipulation. When I first started developing this technique my instinct was to use absolute positioning to slide the inner element in and out as necessary, essentially “filling” the container with the inner element and hiding the unnecessary parts.
This method worked fine when I was only working with simple blocks. As soon as I started working with non-square shapes it fell completely flat. The points between 0 and 100 never lined up correctly. Searching for a new solution, I struck on the idea to align the inner element with the edge of its parent and alter its width/height depending upon the value being displayed. By doing so we gradually reveal the background image of the inner element, allowing it to line up perfectly with the background element of its parent.
What we will do is create two images. The first image will represent the empty state of our visualization and the second will be the completely full state. The empty state will be applied as the background of the gauge element and the full state as the background of the progress indicator element. Technically speaking, I’m only going to create one image with both states in it. For more information on why I’ve chosen to do this, you can refer to CSS Sprites: Image Slicing's Kiss of Death, an excellent article by David Shea. You can view the image here.
The CSS for the container is as follows:
.gauge{ width: 77px; height: 442px; position:relative; background: #333 url(gauge.gif) top left no-repeat; } The CSS here is pretty trivial, but do notice that we specify a hex color value in the background property. This hex color increases the technique's accessibility and will provide a solid reference color to those who have disabled images.
For the inner element:
.gauge .current-value { position:absolute; left: 0px; bottom: 0px; height: 50%; text-align:center; width: 100%; background: red url(gauge.gif) bottom right no-repeat; } Because this animation is vertical I have positioned the inner element at the bottom of the container and positioned the background element at the bottom of the inner element. This technique works just fine as a horizontal animation as well, with the primary difference being where the internal element is anchored to its parent. As before, we specify a color in the event that images are disabled in the user’s browser.
From here, the only property we care about is height. In our example it is set to 50%. The entire visualization hinges on this value being accurate. You may be tempted to scrape the value from the XHTML p element with JavaScript and set the height from there, but that alienates your users with JavaScript disabled. Therefore, your options are to set this dynamically with a backend script or update it by hand. Either way is exponentially favorable to creating 100 distinct images for the intermittent states.
The last step is to hide the textual value.
.gauge .current-value * { display:none; } Note: I am not here to get into a debate on the best way to hide text.
This step is optional, you may wish to leave the textual equivalent of your data displayed over the top of the visualization. I have chosen to hide it, but users who come along with CSS disabled will still be able to view the progress of the fundraiser.
At this point, you can stop. You have created a compelling visual representation that is highly reusable and degrades gracefully in the face of antiquated technology, all using skills already at your disposal. Take a look at the example page to see the results. However, if you want to add a finishing touch you can sprinkle in some JavaScript animation.
Working The JavaScript

Now that we have our gauge built, we will add an unobtrusive layer of JavaScript to animate the mercury of our gauge from zero to its current state. Those users who do not have JavaScript enabled will not see this, but those who do will be presented with a nice animation to complete the temperature gauge visualization.The first thing we will do is setup an object literal to contain our gauge animation. This will help us maintain scope and increase portability if we need to extend our JavaScript.
var gauge = {} We are going to need to keep track of two things: the document node we’re animating and the height at which we will need to stop animating. We don’t know either of these things at the time of creation, so we’ll create variable place holders for now.
var gauge = { targetHeight : 0, progressMeter : new Object(), } My own preference when working with JavaScript object literals is to create an init() method to kick start whatever it is I will be using them for. In this case, init() will need to do three things:
  1. Verify the user’s browser supports the Document Object Model (DOM) specific methods we will be using
  2. Reset the value of the gauge to 0
  3. Animate the gauge to its final value
As such, our init() method will look like:
init : function(){ if(!document.getElementByID) return false; this.resetValue(); this.animateGauge(); } The first line checks to see if we can use document.getElementById and aborts if we cannot. This will exclude all browsers that do not support the standard DOM. The next two lines are for methods we have not written yet, which is something we will do now.
resetValue : function(){ this.progressMeter = document.getElementById("campaign-progress-current"); this.targetHeight = this.progressMeter.offsetHeight; = "0px"; } This method grabs the node we wish to animate and stores it in the progressMeter variable we set above. It then grabs the current height of the node and saves it as the target height we will be animating to. Finally, it sets the height of the node to 0. Once we have done all of this, we are ready to animate the mercury to its current value.
animateGauge : function(){ var currHeight = this.progressMeter.offsetHeight; if(currHeight == this.targetHeight){} else{ var interval = Math.ceil((this.targetHeight - currHeight) / 10); = currHeight + interval + "px"; setTimeout("gauge.animateGauge()",30); } } This function is where the magic happens. It first grabs the current height of the node and compares it to the target height and halts the animation if they are equal. If they are not equal, it runs a very simple formula to calculate an animation interval which is appended to the node’s current height. The formula we use creates a very simple illusion of easing, which is more natural to the eye than a simple linear animation. Once it’s done that, it waits 30 milliseconds and calls the animateGuage() again. This process, known as recursion, will repeat until the gauge has reached its target value. Now all we need to do is call the init() function once the window has loaded.
window.onload = function(){ gauge.init(); } Note: I fully acknowledge that this is a very primitive onload function and should not be used in production. However, that debate is long and not appropriate to this article. Because I have used this technique you may notice some initial flickering.
Putting it all together, the final code looks like this:
window.onload = function(){ gauge.init(); } var gauge = { targetHeight : 0, progressMeter : new Object(), init : function(){ if(!document.getElementById) return false; this.resetValue(); this.animateGauge(); }, resetValue : function(){ this.progressMeter = document.getElementById("campaign-progress-current"); this.targetHeight = this.progressMeter.offsetHeight; = "0px"; }, animateGauge : function(){ var currHeight = this.progressMeter.offsetHeight; if(currHeight == this.targetHeight){ } else{ var interval = Math.ceil((this.targetHeight - currHeight) / 10); = currHeight + interval + "px"; setTimeout("gauge.animateGauge()",30); } } } That’s it. Take a look at the final example to see this at work. This particular data visualization example is very simple, but it is easily extendable and limited only by your ability to create graphical representations of data. With slightly more complicated JavaScript, you can create very dynamic animations on top of this basic XHTML/CSS. As I stated in the introduction, this technique should probably not be used for large data visualization dashboards or situations requiring extensive animation. However, it is excellent at providing progressive enhancement for situations calling for low-to-moderate levels of data visualization.
</img> </img>