Posts from Tuesday, June 12th, 2012

Combining ‘nth-of-type()’ With Negation

Published 11 years, 10 months past

I just recently came across one of those things that’s really obvious once you stop to think about it, and might be really obvious if you think about it from the right angle, but can trip you up if you come at it from a slightly different direction.

Consider the following two rules, which are equivalent:

li:not(.skip):nth-of-type(odd);
li:nth-of-type(odd):not(.skip);

First off, the order doesn’t matter.  The result is exactly the same no matter which way you write it, which is why I wrote the same thing twice.

Either way, the selector will select the odd-numbered li elements that share a common parent, but not those which have a class of skip.  What it does not do is select the odd-numbered li elements without a class of skip that share a common parent.

Did you catch the distinction?  Natural-language ambiguity (languaguity?) may obscure the precise meaning.  Here’s some example markup (adapted from a test file I set up):

<ul>
   <li>Item 1</li>
   <li>Item 2</li>
   <li class="skip">Item 3</li>
   <li>Item 4</li>
   <li>Item 5</li>
   <li>Item 6</li>
</ul>

The list items selected by either of the previous selectors will be numbers 1 and 5.  Numbers 4 and 6 will never be selected, because they are not odd-numbered members of this set of li elements.  Remember, nth-of-type() refers to element types, as in li or p or h4.  It doesn’t refer to “this type of thing that I am trying to describe here in this whole selector”.

Another way of stating this is that the negation pseudo-class does not act as a filter for the :nth-of-type() portion of the selector.  There is no “do this, then that” ordering of pseudo-classes.  They must both apply, considered independently of each other, for an element to be matched.  (This may remind you of the effects caused by the lack of element proximity, though the root causes are rather different.)

There is a (fairly solid-looking) proposal in CSS4 Selectors called :nth-match() that should allow authors to set up a condition where list items 4 and 6 get selected — for example, :nth-match(odd of li:not(.skip)), unless of course the syntax gets changed — but that’s for the future.  For now, if you need to select every other element of a matched set, you’ll need some scripting to help you out.


Browse the Archive