The IE8 "hover" Bug: The Most Awesome IE Bug Ever?
Numbered Lists
When you want to create a numbered list in HTML, you probably use the <ol> and <li> tags. These work well most of the time, and are pretty flexible when paired up with CSS. You can have the numbers be decimals, alphabetic characters (lower or upper), roman numerals (lower or upper), and more, or you can hide them altogether.
But sometimes you can’t (or just don’t want to) use <li> tags to structure your markup, but you still want automatic numbering. So CSS 2.1 came up with this swanky idea to allow automatic numbering on any element: http://www.w3.org/TR/CSS2/generate.html. It also allows decimal, alpha, roman, and other numbering formats, which is great. And it’s officially supported in Firefox 4+, Google Chrome 10+, Safari 5+, and IE8+, which is also great-ish!
Here’s an example of how to use it that someone else did: http://timmychristensen.com/css-ordered-list-numbering-examples.html
You know what’s funny, though? It doesn’t really work that well in IE8, and you have to add some hacks to get it to work in IE9, too: http://stackoverflow.com/questions/5584500/ordered-list-showing-all-zeros-in-ie9. Oh, and the hacks don’t really work reliably, as you can see from people’s responses in that post.
The Bug
I love this bug. It’s just bafflingly awesome.
- IF you are using IE8 (the native version, not IE9 in IE8 compatibility mode!),
- AND you are using the CSS ordered list numbering approach described above,
- AND the html that has the classes that use the “counter-reset” and “counter-increment” CSS attributes is HIDDEN when the page loads,
- THEN whenever that hidden html is displayed,
- ALL of the automatic numbers will be ZERO,
- BUT ONLY IF THE CSS :hover PSEUDO-CLASS IS USED ON THAT PAGE!
Okay, why am I yelling? Because it’s that weird. If you have CSS like this:
a:hover { text-decoration: underline; }
The numbers will all be zero in IE8. If you remove the :hover, or change it to any other pseudo-class (like :active), the numbers are correct.
Here is a self-contained code snippet that exhibits the problem. Remember, this only happens in IE8 NATIVE, and not in compatibility mode.
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
.ol-1-ctr {
counter-reset: ol1 0;
}
.ol-1 {
margin-left: 24px;
}
.ol-1:before {
display: inline-block;
font-weight: bold;
float: left;
width: 24px;
margin-left: -24px;
content: counter(ol1) ". ";
counter-increment: ol1;
}
a:hover { text-decoration:underline; }
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
$('#step-2').hide();
$('#show-step-2').click(function() {
$('#step-1').hide();
$('#step-2').show();
});
$('#show-step-1').click(function() {
$('#step-2').hide();
$('#step-1').show();
});
});
</script>
</head>
<body>
<a id="show-step-1" href="#">Show Step 1</a>
<a id="show-step-2" href="#">Show Step 2</a>
<div id="step-1">
<div class="ol-1-ctr">
<div class="ol-1">Step 1 Item 1</div>
<div class="ol-1">Step 1 Item 2</div>
<div class="ol-1">Step 1 Item 3</div>
</div>
</div>
<div id="step-2">
<div class="ol-1-ctr">
<div class="ol-1">Step 2 Item 1</div>
<div class="ol-1">Step 2 Item 2</div>
<div class="ol-1">Step 2 Item 3</div>
</div>
</div>
</body>
</html>
The Fix
So, the support is dodgy in IE overall. And, IE7 doesn’t support the “counter-reset” and “counter-increment” at all.
I had to come up with another approach.
First, I thought I could just remove all instances of the :hover pseudo-class from my CSS, but that’s heavy-handed, and hover effects are pretty darn useful for buttons. Plus, my project was using jQuery UI, which is littered with :hovers.
Next, I tried to get fancy with CSS, by not hiding anything when the page loads, so the numbering still works. But that meant using CSS absolute positioning and top/left values of –10000px to “hide” the divs by putting them outside of the visible window. Disgusting, and not really something you can do easily if you are using other JavaScript frameworks.
The answer was clearly JavaScript.
The concept was simple:
- Use a div with a class that represents a container for a numbered list (much like the <ol> tag does).
- Sequentially number any items in that container with the class “ol-x”, where “x” represents the nesting level.
- Allow for alphabetic characters and roman numerals as numbering formats, like the default outline numbering in Microsoft Word.
- Handle up to 9 levels of nesting, because more than that is ridiculous.
I use jQuery for my solution, because it made this really easy.
So here is a self-contained solution that solves this problem, and works in IE8 (and IE7 too!). I’ve got some sample HTML to show nine levels of nesting, for fun:
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
.dn
{
display: inline-block;
font-weight: bold;
float: left;
width: 24px;
margin-left: -24px;
}
.ol-1, .ol-2, .ol-3, .ol-4, .ol-5, .ol-6, .ol-7, .ol-8, .ol-9,
.ol-1-ctr .ol-2-ctr, .ol-2-ctr .ol-3-ctr, .ol-3-ctr .ol-4-ctr,
.ol-4-ctr .ol-5-ctr, .ol-5-ctr .ol-6-ctr, .ol-6-ctr .ol-7-ctr,
.ol-7-ctr .ol-8-ctr, .ol-8-ctr .ol-9-ctr {
margin-left: 24px;
}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
var DynamicNumbering = (function() {
// Private Variables
var open = '<div class="dn">', close = '.</div>';
var alpha = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
var roman = ['i','ii','iii','iv','v','vi','vii','viii','ix','x','xi','xii','xiii','xiv','xv','xvi','xvii','xviii','xix','xx'];
// Private Functions
var getLevelDisplay = function(level, index) {
var display;
switch (level) {
case 1:
case 4:
case 7:
display = index + 1;
break;
case 2:
case 5:
case 8:
display = alpha[index];
break;
case 3:
case 6:
case 9:
display = roman[index];
break;
default:
display = 'X';
}
return open + display + close;
};
// Public Functions
return {
execute: function() {
$('.ol-1-ctr').each(function() {
$('.ol-1', this).each(function(i) { $(this).prepend(getLevelDisplay(1, i)); });
});
$('.ol-2-ctr').each(function() {
$('.ol-2', this).each(function(i) { $(this).prepend(getLevelDisplay(2, i)); });
});
$('.ol-3-ctr').each(function() {
$('.ol-3', this).each(function(i) { $(this).prepend(getLevelDisplay(3, i)); });
});
$('.ol-4-ctr').each(function() {
$('.ol-4', this).each(function(i) { $(this).prepend(getLevelDisplay(4, i)); });
});
$('.ol-5-ctr').each(function() {
$('.ol-5', this).each(function(i) { $(this).prepend(getLevelDisplay(5, i)); });
});
$('.ol-6-ctr').each(function() {
$('.ol-6', this).each(function(i) { $(this).prepend(getLevelDisplay(6, i)); });
});
$('.ol-7-ctr').each(function() {
$('.ol-7', this).each(function(i) { $(this).prepend(getLevelDisplay(7, i)); });
});
$('.ol-8-ctr').each(function() {
$('.ol-8', this).each(function(i) { $(this).prepend(getLevelDisplay(8, i)); });
})
$('.ol-9-ctr').each(function() {
$('.ol-9', this).each(function(i) { $(this).prepend(getLevelDisplay(9, i)); });
});
}
};
})();
DynamicNumbering.execute();
});
</script>
</head>
<body>
<div class="ol-1-ctr">
<div class="ol-1">First 1</div>
<div class="ol-1">First 2</div>
<div class="ol-2-ctr">
<div class="ol-2">Second 1</div>
<div class="ol-2">Second 2</div>
<div class="ol-3-ctr">
<div class="ol-3">Third 1</div>
<div class="ol-3">Third 2</div>
<div class="ol-4-ctr">
<div class="ol-4">Fourth 1</div>
<div class="ol-4">Fourth 2</div>
<div class="ol-5-ctr">
<div class="ol-5">Fifth 1</div>
<div class="ol-5">Fifth 2</div>
<div class="ol-6-ctr">
<div class="ol-6">Sixth 1</div>
<div class="ol-6">Sixth 2</div>
<div class="ol-7-ctr">
<div class="ol-7">Seventh 1</div>
<div class="ol-7">Seventh 2</div>
<div class="ol-8-ctr">
<div class="ol-8">Eighth 1</div>
<div class="ol-8">Eighth 2</div>
<div class="ol-9-ctr">
<div class="ol-9">Ninth 1</div>
<div class="ol-9">Ninth 2</div>
<div class="ol-9">Ninth 3</div>
</div>
<div class="ol-8">Eighth 3</div>
</div>
<div class="ol-7">Seventh 3</div>
</div>
<div class="ol-6">Sixth 3</div>
</div>
<div class="ol-5">Fifth 3</div>
</div>
<div class="ol-4">Fourth 3</div>
</div>
<div class="ol-3">Third 3</div>
</div>
<div class="ol-2">Second 3</div>
</div>
<div class="ol-1">First 3</div>
</div>
</body>
</html>
Improvements
This is not beautiful code by any means, but it works for now.
I’m updating this script to be more generic, handle alternate numbering formats (like WBS), and handle nesting more gracefully.
Maybe I’ll post that when it’s ready?
In the meantime, I hope this helps someone out.
Leave a comment
Recent Posts
- iOS Unit Testing With OCMock
- Why Stakeholders Need To Be Involved In Scrum
- NuGet Config File Transformation Causes Duplicate Entries On Update
- Load Testing with Locust on Windows
- Writing A Custom LINQ Provider With Re-linq
- AutoMapper Profile Organization
- Rails 3.2: A Nested-Form Demo Part 4: Switch to Targeting Computer!
- SharpRepository: Configuration
- Rails 3.2: A Nested-Form Demo, Part 3: We’re Starting Our Attack Run!
- Rails 3.2: A Nested-Form Demo, Part 2: Accelerate to Attack Speed!
- Rails 3.2: A Nested-Form Demo, Part 1: All Wings Report In!
- iOS Behind the Curve
- Distributed Transaction Coordinators, Port 135, and Firewalls – Oh My!
- SharpRepository: Getting Started
- Find Performance Problems Using JMeter, MySQL and Xdebug/Webgrind
- Taming Hot Key Context Shifting When Running A Windows VM In Virtualbox On OSX
- Integrating Twitter’s Bootstrap Into Your Project
- Mobile payments, tags and more using NFC
- Stress Pig
- Dear Client Services, What Works?
- What Would Steve Do?
- Still Using Fiddler to Test & Debug Your REST Services?
- Write-through and Generational Caching Make a Great Team
- Thinking Recursively
- Development Incentives, What’s the Payoff?
- How do you like them Apples?
- “Optional” Software Development Practices Series — Code Review
- Adding Images to Select Lists in MVC3
- “Optional” Software Development Practices Series
- You Get What You Pay For…
- Outsourcing Safety Tips
- Facebook IPO
- The Ballad of Tim Toady
- The Little Schemer
- Newsflash: Mom leaves tech job at 5p.m.
- Flashback!
- I <negative_emotion> Windows 8!
- Prefix vs. Postfix Increment and Decrement Operators in C++
- Corporate videos: viral boon or epic fail?
- Recruitin’ Time!
- Reference vs. pointer parameters in C++
- The IE8 "hover" Bug: The Most Awesome IE Bug Ever?
- When is perfect perfect enough?
- SOPA/PIPA: Anti-Censorship Protest or Techies Revenge?
- A Decade of Fairway
- Handling Session Timeout Gracefully
- Generating Software Diagrams
- The Audacity of Nope
- The Origins of Culture
- Scrum Overview in Prezi – not another boring slideshow
- Numbers don’t lie: LinkedIn Statistics
- What is your favorite software development tool?
- Best Practices for Selecting Onshore, Nearshore or Offshore Information Technology Outsourcing (ITO) Providers
- Sign of the Times
- Advantages and Risks of Offshoring, Nearshoring or Onshoring
- Does Outsourcing Mean Offshoring?
- Too little, too late?
- New Favorite Lunch Spot
- Why should I care about functions as first-class citizens?
- PHP Remote Debugging with XDebug and NetBeans
- Installing SubText with Web PI
- ROI Primer
- Learn Domain-Driven Design
- Learn Behavior-Driven Development
- Mario Kart Tournament
- F# in 90 Seconds
- Website Vulnerabilities
- Scrum Overview
- Language Club
- Top 12 Favorite Podcasts Ever…
- Fairway Dart Tournament
- Learn Lean Software Development and Kanban Systems
- Android – Eclipse Quick Start
- Learn Functional Programming
- Backup & Restore Strategy
- Smartphone Screens – Another Wireless Variable
- Wireless Application Market
- Head First AOP





