CSS3 Structural Pseudo-class Expressions Explained

You probably know that the CSS3 spec includes a number of structural pseudo-classes. Four of these pseudo-classes use function-like syntax that allow an argument to be passed in using parentheses. These are:

The purpose of the parentheses is to allow one or more elements to be selected based on a keyword (either odd or even), an integer, or an expression.

The keywords and the integer are pretty straightforward. A value of “even” or “odd” selects the even or odd elements, respectively, and an integer selects the nth of the targeted element. In other words, “li:nth-child(4)” means the “4th” list item will be selected.

The expressions, on the other hand, are a little more complicated, but not too bad once you mess around with them a little.

Expression Syntax

The basic syntax for a pseudo-class expression looks like this:

li:nth-child(an+b) {
	/* styles here */
}

Except for the “n”, the above is not valid, but serves to illustrate the expression in algebraic-like notation. In that expression, each of the letters “a” and “b” would be integers. Here’s what these letters represent:

What is the “b” part?
The “b” part of the expression is an integer that tells the browser what is the first of that type of element to select. Thus if the “b” part is “4” then the “4th” element will be selected first, and all preceding elements are ignored.

What is the “a” part?
The “a” part of the expression is an integer that tells the browser which of those types of elements to select after the first one has been selected. So if “a” is represented by the integer “3”, and “b” is still “4”, then that means the 4th of that type of element will be selected first, and then every 3rd instance of the element will be selected afterwards, until no more can be found.

What is the “n” part?
As alluded to above, the “n” part doesn’t get changed when you write your expression. The “n” is an indicator to the browser to identify the “a” part. This will make more sense in the real examples below.

Some Real Examples

Below are some legitimate examples using different pseudo-classes. Each example is followed by an explanation of what that example does. Keep in mind that, for all of these structural pseudo-classes, the elements selected are siblings. That is, they are children of the same parent element. This means the expression begins counting anew for each parent that contains the selected elements.

Expression: 4n+2

li:nth-child(4n+2) {
	background: hotpink;
}

↑ Selects the 2nd list item, and every 4th list item after that.

Expression: 5n-1

li:nth-child(5n-1) {
	background: hotpink;
}

↑ Selects the -1st list item, and every 5th list item after that. Because the first-selected element is a negative number (-1), you have to imagine a sort of “ghost” element appearing before the real list of elements, and you would also have to include a zero-level element that gets counted when every “5th” element begins to be counted.

Expression: -2n+7

li:nth-child(-2n+7) {
	background: hotpink;
}

↑ In this example, the “b” part is given a negative integer. This will select the 7th list item and then every -2nd list item after that. This means that no list items will be selected after the 7th one. But instead, after selecting the 7th one, the count will reverse and go back up the list, selecting every 2nd element.

Expression: 4n+7

li:nth-last-child(4n+7) {
	background: hotpink;
}

↑ This one uses the “nth-last-child” pseudo-class, which means the expression starts at the bottom of the list. This will select the 7th-last list item (i.e. 7th from the bottom of the list) and every 4th-last list item after that.

Expression: 3n-2

li:nth-last-child(3n-2) {
	background: hotpink;
}

↑ Again, we’re using “nth-last-child”, but this time we’re combining it with a negative integer. This example selects the -2nd-last list item (meaning you have to imagine a “ghost” zero item along with 2 other “ghost” elements) and every 3rd-last list item.

Expression: 5n+3

p:nth-of-type(5n+3) {
	background: hotpink;
}

↑ This example uses the “nth-of-type” pseudo-class. This means only the referenced element will be included in the expression. In this case, we’re targeting paragraph elements. So this will select the third paragraph element then every 5th paragraph after that.

Expression: 2n+8

div:nth-last-of-type(2n+8) {
	background: hotpink;
}

↑ This example uses “nth-last-of-type”, which selects the last element of that type of element. In this case, we’re targeting <div> elements. So all other elements will be ignored, even if they are in between. This will select the 8th-last <div> element, and every 2nd last <div> element after that.

Browser Support and Other Notes

Here are some things worth mentioning about these expressions:

  • Browser support is very good; the only significant lack of support is (and you’ll be shocked to hear this) IE6-8.
  • There are older browsers like Firefox 3.0, Safari 3.1 and Opera 9 that don’t support these but their market shares are so low that for most people this wouldn’t be a factor
  • You can polyfill support for these using Selectivizr but read this before you do
  • If you use the same integer for “a” and “b”, then you can omit the “b” part in the expression; in other words, 3n+3 would have the same result as 3n
  • If you use “1” for the “a” part, you can omit it but leave the “n”; in other words, 1n+5 is the same as n+5
  • If you use a zero for the “a” part, you can omit the “a” part altogether; in other words, 0n+3 is the same as 3

(Thanks to Ana in the comments for helping me undiscombobulate the logic in a few of those bullet points)

Advertise Here

14 Responses

  1. Ana:

    If you use “1″ for the “a” part, you can omit it; in other words, 5n+1 is the same as 5n
    If you use a zero for the “b” part, you can omit the “b” part altogether; in other words, 0n+3 is the same as 3

    Actually, it’s an+b, so 1n+1 is the same as n+1 and 3n+0 is the same as 3n

    • Good catch. My bad. I inadvertently switched “a” with “b” in those bullet points. I Corrected them. Should make sense now.

      Thanks!

      • Ana:

        The first one still not right. 5n+1 is certainly not the same as 5n.

        It’s wasn’t about a and b being switched, just that the examples weren’t the right ones.

        If a=1, you can omit writing the 1 before the n (any number n multiplied with 1 is still n).
        With a=1, you replace that in an+b. You get 1n+b, which is equivalent to n+b

        If b=0, you can omit adding it (because adding 0 to any number results in having the same number).
        With b=0, you replace that in an+b. You get an+0, which is equivalent to an.

        It’s also true that when a=0 you can omit the entire an part, because it is zero.

        • Uh oh…..getting… dizzy…. :)

          You’re right. I was not thinking that through properly. I could have sworn I tested those, too. Thanks. Corrected. Until you tell me it’s *still* wrong. :)

          • Ana:

            May I say it? Still…

            The logic behind 3n+3 being the same as 3n is actually a bit different. It’s not omitting a. If a=b, then an+b becomes an+a, which is the same as a(n+1). And it doesn’t matter whether you have n or n+1 there, so a(n+1) can be simply written as an.

            Anyway, a simple test can prove it to anyone who might have doubts :)

            Plus, it’s missing the b=0 situation :P I explained that in the previous comment as well.

          • Woops… again, I meant to say “omit b”, not “a”. I seem to have transposed “a” and “b” in my mind when writing those bullet points.

            I think that has something to do with the fact that the “b” part is actually the first element that gets selected, which makes it kind of confusing at first glance. For example, to me, this would make more sense:

            
            li:nth-child(4+3n) {
            
            }
            

            Because then it says quite clearly that you should select the 4th element, then every third element after that (thus it becomes a+bn).

            Maybe there are reasons why it’s reversed in the notation, but my superficial understanding of it is not helping me grasp those reasons. :)

  2. Ana:

    Also, combining nth-child with nth-last-child helps with selecting elements in the middle – example.

    • Yeah, to be honest, I was thinking of posting a separate “tricks with pseudo-class expressions” or something like that, which would highlight those types of quirky examples. We’ll have to see if I can come up with enough to warrant a separate post.

  3. Ana:

    The first thing that comes to mind is that it might have something to do with the convention of writing polynomials in one variable starting with the term that has the highest degree and ending with the one that has the lowest degree. Like this.

    I find it helpful to keep in mind that the first value for n is 0 (in which case the entire first term an is 0, no matter what value a might take) and to actually compute the result mentally for the first few values of n (0, 1, 2) just to make sure I got everything right.

  4. Wizzyboom:

    So basically, if a=Ana and then b=Louis and n=(number of times you’ve been corrected) then (an – b) would equal something b=OWNED. :P

    Regardless of the equations, this is a great article and I think it does a good job, with a little help, ;) explaining these powerful new pseudo-classes!

    Thanks for your time Louis, keep it up!!

  5. Awesome. Finally a good explanation. Until now I’ve always “I don’t need this”ed these selectors. Not anymore!

  6. Francisc:

    Good article, it would be easier to understand with live demos under each example.

Leave a Reply

Comment Rules: Please use a real name or alias. Keywords are not allowed in the "name" field. If you use keywords, your comment will be deleted, or your name will be replaced with the alias from your email address. No foul language, please. Thank you for cooperating.

Instructions for code snippets: Wrap inline code in <code> tags; wrap blocks of code in <pre> and <code> tags. When you want your HTML to display on the page in a code snippet inside of <code> tags, make sure you use &lt; and &gt; instead of < and >, otherwise your code will be eaten by pink unicorns.