Go back to home page of Unsolicited Advice from Tiffany B. Brown

How the :nth-child() and :nth-of-type() pseudo-classes work

10 Aug 2015: Read the follow-up post How :nth-of-type() works … a redux. It clarifies and corrects this post.

One of the things I most enjoyed about writing CSS Master was that I got to do deep-dives on aspects of CSS that I hadn't fully explored. I found the :nth-* pseudo-classes: :nth-child(), :nth-last-child(), :nth-of-type() and :nth-last-of-type() especially interesting. They're as useful as they are confusing, and I hope to shed some light on them with this post.

With :nth-* selectors, we can choose elements based on their position in the document sub-tree. We can select the fifth paragraph in an article. We can change the background of every other row in a table. We can choose every third child, starting after the fifth. Or we can select the third-from-last child.

In addition to the even and odd keywords, all of the :nth-* pseudo-class functions accept an argument in the form An+B, where:

  • A is a step interval;
  • n is zero, or a positive integer;
  • B is an offset indicating how many elements we should skip before applying the styles;
  • A and B are integers; and
  • the initial value of n is 1.

If just reading that made sense to you, then you, my friend, are a smarty-pants. For the rest of us, some illustrations will help. All of the images used below are SVG documents with embedded CSS. You can view the source of any of them to learn more.

Take the ten hexagons in Figure 1. Each has a hex class name attached.

Figure 1: Ten hexagons.

Now, let's say we want to select every third hexagon. To do that, we'd use .hex:nth-child(3n). As explained above, 3 is a step interval and n is the index. The step interval functions almost like a multiplier for the index n. Starting at 1, our selector targets the 3rd, 6th, and 9th hexagons below: 3×1 = 3, 3×2 = 6, 3×3 = 9, etc.

Using :nth-last-child() works similarly, but the count begins with the last element in the subtree. Figure 3 shows what happens when we use .hex:nth-last-child(3n). The 3rd from last, 6th from last, and 9th from last hexagons are selected.

How about we select the 7th hexagon and just the 7th hexagon. To do that, we could use .hex:nth-child(0n+7). Here, our step interval is zero, and our offset is 7. Since (0×1)+7=7, (0×2)+7=7 and (0×102)+7 still equals 7, only our 7th child will be selected.

Using 0n+7 is kind of awkward-looking, though, right? Well, according to the rules of An+B syntax, when A=0, you can leave out An. We can simplify .hex:nth-child(0n+7) to .hex:nth-child(7).

Negative offset values are also valid. For example, we can select every third element beginning with the first one, using :nth-child(3n-2).

Using nth-of-type and nth-last-of-type

This section is only partly correct. Please read the follow-up post.

The :nth-of-type() and :nth-last-of-type() pseudo-classes work similarly. But they match elements of the specified selector type only when it's the nth child. Here's another list of ten items. This time, half of our elements have a hex class, the other half star.

To select the second star, you might think that we'd use .star:nth-of-type(2). But as you can see in Figure 10, that doesn't work. Using .star:nth-of-type(2) is like saying ** match the second child if its class name is star, ** and not match the second star.

To actually match the second star, we'd need to use .star:nth-child(4): match the fourth child if its class name is star.

Selecting a range of elements by adding :not()

Perhaps the coolest thing we can do is combine these :nth-* pseudo-classes with the :not() pseudo-class to select a range of elements. Using :not() inverts whatever selector was passed as an argument. For example, using .hex:not(:nth-child(n+4)) matches the first three elements.

We can use this to our advantage to select a range of child elements by chaining the :nth-child() and :not() pseudo-classes. Let's select the second, third, and fourth items in the sub-tree.

.hex{
    fill:#9C27B0;
}

.hex:nth-child(n+2):not(:nth-child(n+5)) {
    fill: #00bcd4;
}

Using .hex:nth-child(n+2):not(:nth-child(n+5)) selects child elements with an index greater than or equal to 2 and less than 5.

Using :nth-child() and :not() to select a range of elements.

To get a better sense of how steps and offsets work with :nth-child(), I've created a tool that helps visualize it. It's similar to nth-test, but IMHO does a better job of explaning the An+B micro-syntax.

Get CSS Master

Did you learn something from this blog post? You might like the latest edition of CSS Master. It contains several nuggets of CSS joy like this one. Buy now from SitePoint