Techy Treasures #5: Fudging CSS Counters in Internet Explorer

Share this article

Chocolate IE Fudge. Mmm mmm!

Have a look at this example, of an ordered list with styled numbering:

The list-item numbering in that example is implemented using CSS Counters, a feature of CSS2 that allows you to count the instances of a particular element (or group of elements identified by selector), and then use pseudo-elements to display the numbers. In its simplest incarnation it can be used to replace the numbering of an ordered list, as in the example above, which adds a little extra styling to the numbering just to illustrate that it’s happening. As it gets more complex it can be used to replace the deprecated start attribute, or to apply numbering systems that are more sophisticated than native list numbering supports, for example, numbering things like 1.1, 1.2 and so on.

The CSS for that example looks like this:

	counter-reset: step;
#css-counters-test li::before
	counter-increment: step;
	content: counter(step) ": ";
	color: green;
	font-weight: bold;

Now a problem with its wider uptake is that, as is so often the case, it isn’t supported in Internet Explorer; not even IE8. However, by using a proprietary feature of IE’s CSS implementation, we can in fact implement counters after all. How? By using the much-vaunted but seldom-used expression property.

The main problem with expression, and superficially the reason why it’s no use for generated content, is that it re-evaluates continually — although it can be used to modify the HTML of an element, if we allow it to continually re-evaluate then we’ll be continually changing the HTML. No good at all. But … we can use expressions in a way that avoids re-evaluation, by using a one-time use expression, invented by Jason Davis.

Have a look at the example page in IE and you’ll see that the numbering is working there too, and that’s done with the following code:

#css-counters-test li
	zoom: expression(
		  (typeof step == "undefined" ? step = 1 : step++), 
		  this.innerHTML = (typeof this.processed == "undefined" 
				? ("<span class=before>" + step + ": </span>") 
				: "") 
			+ this.innerHTML, 
		  this.processed = true, 
		  this.runtimeStyle.zoom = "1"

#css-counters-test li span.before
	color: darkblue;
	font-weight: bold;

The theory behind the one-time use expression is simple: an expression can take any number of statements, but only the last one is returned. So in the final statement we override the expression property by writing to runtimeStyle (which is a writeable version of currentStyle, IE’s computed-style property) and that removes the expression property and thereby prevents it from re-evaluating — it doesn’t re-evaluate because it isn’t there anymore!

(Actually, for reasons I haven’t been able to fathom, each of these expressions in fact evaluates twice, and that’s why I’ve used a processed property to prevent the number being written in twice (if I had to guess, I’d say that IE is evaluating the open tag and the close tag for each instance of the selector; but that’s just a guess). But anyway, a simple caveat like that is not a problem to add. The use of the zoom property here is arbitrary — we can use any CSS property at all, as long as IE supports it; I’ve chosen zoom so that I can add a default value at the end which doesn’t otherwise affect the element’s appearance. Another good choice might be height with the value auto.)

So there we have it. While a normal expression, which evaluates continually, is no use for modifying HTML, what we have here can be used for that, and can therefore be used to implement CSS counters!

James EdwardsJames Edwards
View Author

James is a freelance web developer based in the UK, specialising in JavaScript application development and building accessible websites. With more than a decade's professional experience, he is a published author, a frequent blogger and speaker, and an outspoken advocate of standards-based development.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week
Loading form