Have a look at this example, of an ordered list with styled numbering: http://www.sitepoint.com/examples/css-counters/
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:
#css-counters-test
{
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!
Related posts:
- A Little-known Way to Replace Some Scripts with CSS Counters Replacing executable scripts with style sheets often improves performance and...
- Techy Treasures #4: What’s inside a dollar function? The $ function is a common feature of all of...
- Techy Treasures #3: When is a mouseout not a mouseout? I've had this little gadget in my toolbox for a...
- Internet Explorer 8.0: The One Month Review, Part 2 IE8 has been available for a month. In the second...
- Will Microsoft Implement HTML5 in Internet Explorer? Microsoft has not rushed to implement HTML5 within Internet Explorer,...







From what I understand, IE8 will no longer support the proprietary CSS expression filter.
http://blogs.msdn.com/ie/archive/2009/03/12/site-compatibility-and-ie8.aspx
What isn’t clear to me is if the IE8 mode determines CSS expression’s availability, or if CSS expression is not available in any mode. I think it’s the latter.
March 31st, 2009 at 12:16 pm
Doesn’t IE8 support generated content?! I thought Microsoft promised 100% CSS2.1 compliance with this version.
Or is it just the CSS3-style syntax (
::before) instead of the CSS2.1 equivalent (:before) that it doesn’t support?March 31st, 2009 at 4:25 pm
I was going to note the same as AutisticCuckoo. As far as I can tell, IE 8 does support counters() correctly, but in your example, it won’t show anything, as it doesn’t understand the css3 notation of
::before, only the css2.1 single column notation (:before).March 31st, 2009 at 4:47 pm
This is helpfull. Thanks.
March 31st, 2009 at 5:06 pm
For the IE example, every time a li is parsed in the document, the expression is evaluated and step increments?
March 31st, 2009 at 9:23 pm
@Tommy – I thought so too, but it doesn’t appear to work. Actually I hadn’t thought to try the different syntax; lemme try that and I’ll post again.
@ionutzp – yes exactly.
April 1st, 2009 at 3:24 am
I thought IE8 got rid of expressions.
http://blogs.msdn.com/ie/archive/2009/03/12/site-compatibility-and-ie8.aspx
Looks like it did. I guess that wraps it up for this article!
April 1st, 2009 at 3:33 am
Nope no joy. Generated content isn’t working in IE8.
April 1st, 2009 at 4:06 am
It doesn’t sound like this will work in IE8 Strict mode as CSS expressions have been removed. As I understand it, they will continue to work in IE8 while in Compatibility mode (IE 5, 7, EmulateIE7, and EmulateIE8 quirks mode).
http://blogs.msdn.com/ie/archive/2008/10/16/ending-expressions.aspx
http://blogs.msdn.com/ie/archive/2009/03/12/site-compatibility-and-ie8.aspx
April 1st, 2009 at 4:26 am
Yeah you’re right .. I hadn’t noticed because compatibility mode is enabled by default for local intranet pages (and of course I tested it on my local network).
How typical of microsoft .. they remove support for a hack without fixing the reason for needing it!
April 1st, 2009 at 6:02 am
@Philippe – I tried it using the CSS2 notation, and it still didn’t work. Can you show me an example of working counters in IE8?
@Michael – not at all :-P It’s still a valid and useful technique for IE7 and IE6 (and IE8 in compatibility mode)
April 2nd, 2009 at 2:15 pm
http://software.hixie.ch/utilities/js/live-dom-viewer/saved/54 – works for me in IE8 although the numbers are 1, 4, 6, 8, 10, 12 instead of 1..6. Clearly that’s not correct.
Also, it’s silly of them to not support the double-colon syntax — I filed a bug about that during the IE7 beta and the IE8 beta but it was rejected both times. Oh well.
April 2nd, 2009 at 5:51 pm
(…especially considering that double-colon syntax works in IE6!)
April 2nd, 2009 at 5:52 pm
These 2 test cases are supposed to work correctly:
http://dev.l-c-n.com/css2/counters-Ordered_list.html
http://dev.l-c-n.com/css2/counters-2.html
Mind you, check with IE 8 release. Right now, I only have Win 7 with a slightly outdated build; the numbering goes wrong. But the release build should work fine.
Note that Opera, up to 10alpha is really wrong, when using
counters()and there are multiple lists on the page, like my second test case.April 2nd, 2009 at 9:47 pm