How the :nth-child()
and :nth-of-type()
pseudo-classes work
: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.
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)
.
Using .hex:nth-last-child(7)
instead matches the 7th from the last element (the fourth hexagon).
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_.
We can take this one step further, and select every other element beginning with fourth one: .hex:nth-child(2n+4)
.
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)
.
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
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.
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