Tiffany B. Brown

07 August 2015

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.

Your browser doesn't support SVG. Please upgrade to one that does if you can.
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.

Your browser doesn't support SVG. Please upgrade to one that does if you can.

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.

Your browser doesn't support SVG. Please upgrade to one that does if you can.

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).

Your browser doesn't support SVG. Please upgrade to one that does if you can.
Figure 4: Using :nth-child() to select a single child element.

Using .hex:nth-last-child(7) instead matches the 7th from the last element (the fourth hexagon).

Your browser doesn't support SVG. Please upgrade to one that does if you can.
Figure 5: Selecting a single child element with :nth-last-child().

Using the offset to skip elements

Adjusting the offset value gives us a lot of flexibility. We can, for instance, select every element but the first three: .hex:nth-child(n+4). Using n+4 says select every element _n beginning with the fourth one_.

Your browser doesn't support SVG. Please upgrade to one that does if you can.
Figure 6: Using the offset to skip elements.

We can take this one step further, and select every other element beginning with fourth one: .hex:nth-child(2n+4).

Your browser doesn't support SVG. Please upgrade to one that does if you can.

Negative step values are valid. We can use them to invert a selection. If, for example, we wanted to select only the first three elements, we can use :nth-child(-n+3).

Your browser doesn't support SVG. Please upgrade to one that does if you can.

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

Your browser doesn't support SVG. Please upgrade to one that does if you can.

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.

Your browser doesn't support SVG. Please upgrade to one that does (if you can).

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.

Your browser doesn't support SVG. Please upgrade to one that does (if you can).

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.

Your browser doesn't support SVG. Please upgrade to one that does (if you can).

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.

Your browser doesn't support SVG. Please upgrade to one that does (if you can).
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 second edition of CSS Master. It contains several nuggets of CSS joy like this one.

Buy now from SitePoint