Tuesday, May 8, 2012

Implementing ":Nth-child" CSS selector that works with IE7 and IE8

Wow, it's been almost 2 years since I wrote my last technical blog entry!  I guess the busier things get, the harder it is to set aside time to document things and "give back" to the developer community.

So I guess since it's been so long I should first mentioned that this is Todd, using my Speednet account at Lottery Post.  I write technical blog posts using this user account from time-to-time (sometimes waiting 2 years or so, *ahem*) in order to share technical thoughts and articles with other Web developers.

I strongly believe in the concept of "giving back" knowledge that is learned through great difficulty, because it will encourage others to do the same.  The end result is that the Web becomes a better place for all the sharing.

Getting to the topic at hand, one of the things developers constantly struggle with is that they want to develop Web sites using all the technologies and methods found in the latest Web browsers, but oftentimes doing so means that people using older Web browsers either cannot see or use the page/feature, or else it's buggy or has worse functionality.

It is very common in Web development to have a minimum browser version to develop for, meaning that all pages are supposed to work and look the same in the minimum (worst) browser specified, as well as all browsers that are better than that.

Up until this year, that "worst" browser has typically been Internet Explorer version 6 (IE6).  By today's standards, it's an absolutely horrendous browser — extremely buggy, unsecure, and lacking features.

But, thanks to all the emphasis made in the industry to get people off of IE6, the percentage of people using IE6 has dropped to a very small number.  (At Lottery Post that number is about 1.5% of all visits to the site.)  Thus, most developers have punted IE6 as the minimum specification, and are now focused on IE7 as the minimum browser to support.

Lottery Post has likewise punted on IE6 and I focus testing on IE7 and above.  IE6 still works in most cases, but I almost never test anything using IE6.  And I have posted a warning message for over two years on Lottery Post that is visible to IE6 users, telling them that their browser is no longer supported.  (Frankly, IE7 users having been seeing a similar message as well, and eventually I'll be dropping support for that version too — but not quite yet.)

As a developer, IE7 is definitely much better than IE6, as far as supporting various important Web technologies, but it is still far from being considered "modern".  So it is still tricky to develop in a cross-platform manner, in which one set of code works exactly the same for all supported browsers. 

That's why a topic like this one is important:  I have come across a technique for designing a piece of CSS code that will work even in IE7, although IE7 typically doesn't support it.

I was trying to specify CSS rules in a Web page using the :nth-child pseudo-class, in order to set styles on table cells according to their order in the row.  For example, to style the third cell in each row of a table that has a class name of "stats", I could use an expression like this:

table.stats tr td:nth-child(3) { color: red; }

In the latest Web browsers, like IE9, Google Chrome, Firefox, Safari, etc., it works perfectly:  each row's third cell has red text.  But, it you try looking at it with IE7 or IE8, the text is not red because the unsupported :nth-child pseudo-class is ignored.

So I just happened to be flipping through one of my favorite CSS books (The Ultimate CSS Reference, by Tommy Olsson & Paul O'Brien), and although it did not present this solution, it provided what I needed to figure it out.

The trick is to use CSS selectors to IE7 and IE8 can process, and string them together.

Instead of quickly jumping straight to the "nth child", you first find the first child element using the :first-child pseudo-class, and then use the adjacient child selector (+) until you reach the child you wanted to style.

Using that technique, which will work in all browsers including IE7 and IE8, the expression changes to:

table.stats tr td:first-child + td + td { color: red; }

It seems pretty simple when you see it, but until you actually try it, CSS developers who are accustomed to CSS expressions constantly driving down a stack of elements (instead of across elements) may not even think it would work.  But it does!

This expression does indeed work like the :nth-child pseudo-class, but not exactly.  If you want it to precisely mimic :nth-child you need to use a universal selector instead of an element name.  For example:

table.stats tr *:first-child + * + td { color: red; }

However, using the previous expression with the element name selectors is better in the case of the table.  You would use the universal selectors if you are unsure what the child elements will be, or what their order will be.

Of course, there are a couple minor exceptions when this technique will not work, as is always the case when programming for IE7.

  • You cannot dynamically insert elements before the first child element, because IE7 will not re-calculate the first child after the page is rendered.
  • You cannot have HTML comments inserted anywhere before the first child or between the adjacient child elements, because IE7 counts it as one of the child elements.

Other than these caveats, you're pretty much good to go.

I hope this helps a developer out there!