CSS progress bars without extra DIVs
While working on the smrtr project I needed to create progress bars to show the completion. However, rather than having independent bars all over the place, I wanted to create bars that could be contained within another element.
The ideal solution would be the possibility to scale a background image to a given width with CSS but this is not available until CSS3 (if then). So another approach was needed:
1. Position absolute & z-index
Intially I approached this by including an additional div within the object to show the progress bar. By setting position:relative; on the container and position:absolute; top:0px; left:0px; on the internal div it was possible to overlay the bar on the container. An additional z-index:1 on the content stacks them up correctly and the bar can then be assigned a width to show the percentage.
This works fine, however, it restricts you from assigning bars to any span elements (can’t put a div inside a span) and gets messy due to the additional divs added. Adding extra elements for an effect is always a last-ditch option, as it complicates the readability of the code.
2. Background-position
So we have option 2. Originally this was option 1, but discarded because it ‘did not work’. A cup of tea later however, and I found a way to make it so it does. Background position positions an image relative to the top-left of the block, and nowhere else. This is a shame because if we could position from the top-right we could just slide the background image backwards and forwards to get the effect.
Setting background-position 0-100% slides it away from the left hand edge, leaving the space in the wrong place. We can set the background-position to -0 to -100%, but only if we know the width of the element and create the background-image to fit. Unfortunately that doesn’t apply here.
So the solution? Create an image that is 50% bar, 50% blank.
When this image is positioned at 50% the division line will the bang on 50% across the container. At background-position: 55% the barline will be 45% across the container. Yes, 45%. The positioning goes the wrong way, but it is proportional and stays so at any size container. All we need to do is subtract the value from 100 before applying the position.
The code can be applied to any element you like:
.percent-complete {
background: url(../img/progressbar-50.gif) repeat-y;
background-position:50%;
}
The source image:
It in action: