nth-child is weird

— 3 minute read

I ran into a CSS gotcha today and it brought up an interesting (and important) question: What's the difference between nth-child and nth-of-type?

Comparing two things or one permalink

Take a look at this sample code to get a feel for what we're talking about.

<style>
p:nth-child(2) {
background-color: green;
}
</style>

<div>
<p>This paragraph is not green</p>
<p>This paragraph *is* green</p>
</div>

You'll notice that we're setting our second p element to have a background-color of green. To do this we use p:nth-child(2). I think the way most people (myself included) would read that selector is "select the second child paragraph". But if we change the markup to look like this:

<div>
<h1>Hello World!</h1>
<p>This paragraph is not green :(</p>
<p>This paragraph is green!</p>
</div>

Suddenly our green background moves to the wrong element. What gives?!

Chris Coyier helpfully explains on CSS-Tricks.

Our :nth-child selector, in "Plain English," means select an element if:

  • It is a paragraph element
  • It is the second child of a parent

Since we've added another child in the form of an h1 tag, we need to now say p:nth-child(3) if we want to select the same element. In my mind that makes the nth-child tag brittle and somewhat counterintuitive.

Thankfully there's an alternative in form of the nth-of-type selector.

p:nth-of-type(2) {
background-color: green;
}

Again, quoting Mr. Coyier:

Our :nth-of-type selector, in "Plain English," means:

  • Select the second paragraph child of a parent

So nth-of-type gives us the functionality we were originally looking for and doesn't require us to change our markup if we add additional child elements which use different tags. That's pretty sweet!

If you want to play around with the idea I've put together a codepen. Enjoy!