CodeinWP CodeinWP

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)

15 Responses

  1. Ana says:

    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.


      • Ana says:

        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 says:

            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 says:

    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 says:

    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 says:

    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. Rudie says:

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

  6. Francisc says:

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

  7. Amit says:

    Nice article.It’s a helpful article

Leave a Reply

Comment Rules: Please use a real name or alias. Keywords are not allowed in the "name" field and deep URLs are not allowed in the "Website" field. If you use keywords or deep URLs, your comment or URL will be removed. No foul language, please. Thank you for cooperating.

Markdown in use! Use `backticks` for inline code snippets and triple backticks at start and end for code blocks. You can also indent a code block four spaces. And no need to escape HTML, just type it correctly but make sure it's inside code delimeters (backticks or triple backticks).