On November 25, 2021, I learned...

:nth-child() counts line breaks as spans in CSS

I was diddling around and working with this markup:

<h1>
  Hidd<span>e</span>n
  <br>
  Br<span>a</span>in
</h1>

I wanted to select the second <span> so, naturally:

.span:nth-child(2) { ... }

And gosh darn it was I surprised that my styles weren’t showing up! Making sure I had the syntax right, I went for:

.span:nth-child(1) { ... }

It worked! More head scratching. Hmm, OK, why not?

.span:nth-child(3) { ... }

By golly, not only did that work but it selected the second <span> in the process, giving me exactly what I had aimed for. CSS apparently selects <br> elements when counting :nth-child(). New to me!

And guess what? It worked when I put any dang element in place <br>. Everything, like <div>, <main>, <input>, <abbr>, <kbd>, you name it.

I can’t find anything in the Selectors Level 4 spec on the :nth-child() side of things. I also looked at the HTML Standard and came up empty on the <span> side of things. I even thought that perhaps it was just a bug with Safari, but it works across the board.

I dunno. Maybe I’m overlooking something obvious. But today I learned that :nth-child() counts all elements when it searches spans. I mean, look, it counts the only <span> inside an element when it’s the last child, like:

<!-- Not that you'd ever do this -->
<h1>
  Hidd<div>e</div>n
  <abbr></abbr>
  Br<span>a</span>in
</h1>

10 Comments

  1. # November 26, 2021

    I think this is exactly what happens. A span:nth-child(3) selector will match only the span that is also its parent’s 3rd child. Your br isn’t counted as a span, it’s counted as a child element.

    But today I learned that :nth-child() counts all elements[.]

  2. # November 26, 2021

    I think :nth-child can be a tricky one to understand. When you write span:nth-child(3), what you’re saying is, “give me all span elements that are the 3rd child of their parents.” So it doesn’t really matter what the siblings of the span are. The same goes for any other element/selector. The CSS level 4 spec lets you do what you’re shooting for here with the “of S” part of the selector (see https://drafts.csswg.org/selectors/#example-3c07f717), but I don’t know if it’s supported anywhere yet. In the meantime, as long as you’re only selecting on a tag name, you could build a selector with :nth-of-type. To get the 2nd span that is a child of h1, you would do this: h1 > span:nth-of-type(2). In case this doesn’t make sense, I put together a codepen: https://codepen.io/kswedberg/pen/XWavPNB

    • # November 26, 2021

      Heck yeah, thanks Karl! You and Bramus nailed it. The distinction between “this is a span that is the third child” and “this is the third span child” is exactly what I was mixing up.

Comments are closed.