by

Better selecting with a better :nth-child()

Reading Time: 3 minutes

So the :nth-child structural pseudo class is mighty handy already since it lets you select based on mathematical rules. But to my surprise, it got handier and no one told me. Which makes me think no one told you, either:

:nth-child() can do filtering now!

A Quick Primer on Structural Pseudo-classesWatermark:FrankMTaylor

So I wrote about the structural pseudo classes thirteen years ago (pikachu shocked face). Structural pseudo-classes are CSS selectors that let you target elements based on the structure of the document. Structural pseudo-classeswatermark: Frank M. Taylor themselves break into a few categories:

Algebraic pseudo-classes
These require an algebraic argument in order select an element by its index within its siblings.
Includes :nth-child(), :nth-last-child(), :nth-of-type()
Ordinal pseudo-classes
These select elements whose index is initial or final within its siblings
Includes :first-child, :last-child, :first-of-type
Parent-child pseudo-classes
These select elements based on the relationship parent and child elements have to each other.
Includes :only-child, :only-of-type, :first-of-type, :empty
Document Pseudo-class
These select based on a fixed point in the document.
Includes :root

If you want to know about all the crazy things you could already do with these, read my article.

:nth-child() Used to Have an Annoying Limitation

Ok, so let’s assume some markup:

<ol>
<li>one</li>
<li class="foo">two</li>
<li>three</li>
<li class="foo">four</li>
<li>five</li>
<li class="foo">six</li>
<li>seven</li>
<li class="foo">eight</li>
</ol>

If you wanted to select the third list item, that’d be li:nth-child(3). Easy peasy lemon-squeezy.

But what if you wanted to select the third .foo? Watermark:FrankMTaylor

You’d be tempted to try this:

.foo:nth-child(3) {
  outline: 1px solid red;
}

But that’s not gonna work. Because what that selector means is, get the third child amongst the siblings that is also .foo.

So :nth-child was limited to purely the document structure.

Now :nth-child() has “filtering”

Ok, back to the same HTML as before:

<ol>
<li>one</li>
<li class="foo">two</li>
<li>three</li>
<li class="foo">four</li>
<li>five</li>
<li class="foo">six</li>
<li>seven</li>
<li class="foo">eight</li>
</ol>

This time, let’s use this magical of keyword

li:nth-child(3 of .foo) {
  outline: 1px solid red;
}

When you use the of keyword, you’re applying a filter.

It’s a filter that tells the browser, reduce the collection to whatever is to the right of of, then apply the argument on the left of of

Let’s start with a basic live demo where you could go about changing that number:

  1. one
  2. two
  3. three
  4. four
  5. five
  6. six
  7. seven
  8. eight

Remember, it’s a filter

Ok, so cool. you can now get the nth-child class of whatever. But you gotta really think about this now.

How many elements do you think will have a yellow background color?

li:nth-child(even of .foo) {
 background-color: yellow;
}

If you thought four, you were wrong.

If you thought two, then you win a prize.

Check it outWatermark:FrankMTaylor

  1. one
  2. two
  3. three
  4. four
  5. five
  6. six
  7. seven
  8. eight

Why are there only two with a yellow background?

Because it first filtered you down to the .foo elements. So the function is now only applying to these:

<ol>
<li>one</li>
<li class="foo">two</li>
<li>three</li>
<li class="foo">four</li>
<li>five</li>
<li class="foo">six</li>
<li>seven</li>
<li class="foo">eight</li>
</ol>

And with those four elements, only two are even:

<ol>

<li class="foo">two</li>

<li class="foo">four</li>

<li class="foo">six</li>

<li class="foo">eight</li>
</ol>

Because it’s a filter.

It’s a very cool filter.

How cool is it?

Who said you had to give it some basic-ass selector? Not me.

You can give it pretty much whatever you want.

What if you wanted an even foo, but not the last even foo?

li:nth-child(even of .foo:not(:last-child)) {
 background-color: yellow;
}

Again, you’re filtering.

You might be wondering, “wait, could I nest my filters?”

Yes, you can nest your filtered nth-child

li:nth-child(2n of :nth-child(3n of .foo)) {
  color: yellow;
}

And in case you were wondering, that is the exact same thing as this:

li:nth-child(6n of .foo) {
  color: yellow
}

So while you could go about nesting your filters ad infinitum, don’t. It’s best if you keep them as simple as possible.

What are the practical applications?

The most obvious application is tables.

Suppose you’ve got some td and th in a table row. You could start your column styles from the first table cell now instead of the first table item.

Go ahead and change the selector to td:nth-child(even) to see what I mean:

1 one uno
2 two dos èr

(BTW, you could also pull this off with :nth-of-type, because it’s a type selector. But not if you were trying to go by class name or attributes)

Beyond tables, I’d be wary of doing this too much. I could see some masonry-style shenanigans where it could be fun, though.

Have Fun and Use it Sensibly

The structural pseudo-classesWatermark:FrankMTaylor are super handy in cases where content is generated dynamically. In my world of content management, structural pseudo-classes are a way to wrangle the chaos that content authors could produce.

But in any instance where you can just put a class on the element and style off of that, choose that option first. These pseudo-classes could definitely get you in trouble if you’re not careful.

Leave a Reply

You don't have to register to leave a comment. And your email address won't be published. If you found a bug, be a gem and share your OS and browser version.

This site uses Akismet to reduce spam. Learn how your comment data is processed.