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

Published 5 years, 8 months ago

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.


  1. Super interesting. I’m assuming you meant the opening and closing tags of the list itself to match though?

  2. Very nice. The idea that the selector, albeit “complicated-looking” is *not* a “program” that runs in sequence is a genuinely helpful clarification! FWIW, I now appreciate it as being more like a pair of overlaid filters, which, when combined, only select those items where each filter has a “hole” that lines up with a hole in the other, allowing the underlying item to be seen. (One of those things where a simple diagram might also help.) Or, in other words, the components of the selector are joined by an implicit logical AND. As in, both (phrase A) AND (phrase B) must be true in order for the element to be selected. I’m surely botching the point–you can do better–but to anyone with a smidgeon of boolean algebra in their background, like old programmers who’ve taken a shine to web design (*cough, cough*), that might likewise help increase the overall level of illumination. Or\\And not. ;-)

  3. Er, um, yes, Brendan. Fixed. Thanks!

    Glad to be of assistance, iDGS!

  4. […] Combining ‘nth-of-type()’ With Negation […]

  5. […] Combining ‘nth-of-type()’ With Negation […]

Leave a Comment

Management reserves the right to edit or remove any comment, especially when abusive or irrelevant to the topic at hand. HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <em> <i> <q cite=""> <s> <strong> <pre class=""> <kbd>


Comment Preview

If you're satisfied with what you've written, then go ahead...