More control over :nth-child() selections with the of S syntax

Pre-filter a set of child elements before applying An+B logic on it.

Bramus
Bramus

The :nth-child() and :nth-last-child() pseudo-class selectors

With the :nth-child() pseudo-class selector it is possible to select Elements in the DOM by their index. Using the An+B microsyntax you get fine control over which elements you want to select.

  • :nth-child(2): Select the 2nd child.
  • :nth-child(2n): Select all even children (2nd, 4th, 6th, 8th, and so on).
  • :nth-child(2n+1): Select all odd children (1st, 3rd, 5th, 7th, and so on).
  • :nth-child(5n+1): Select the 1st (=(5×0)+1), 6th (=(5×1)+1), 11th (=(5×2)+1), … child.
  • :nth-child(5n+2): Select the 2nd (=(5×0)+2), 7th (=(5×1)+2), 12th (=(5×2)+2), … child.

But, there’s more creative selections you can do, if you omit the A parameter. For example:

  • :nth-child(n+3): Select every child from the 3rd one up (3rd, 4th, 5th, and so on).
  • :nth-child(-n+5): Select every child up to the 5th one (1st, 2nd, 3rd, 4th, 5th).

Combine a few of these :nth-child() selections and you can select ranges of elements:

  • :nth-child(n+3):nth-child(-n+5): Select every child from the 3rd one up to the 5th one (3rd, 4th, 5th).

Using :nth-last-child() you can do similar selections, but instead of starting to count from the start, you start counting from the end.

Pre-filtering selections with the of S syntax

New in CSS Selectors Level 4 is the ability to optionally pass a selector list into :nth-child() and :nth-last-child().

:nth-child(An+B [of S]?)
:nth-last-child(An+B [of S]?)

When of S is specified, the An+B logic is only applied onto elements that match the given selector list S. This essentially means that you can prefilter children before An+B does its thing.

Browser Support

  • Chrome: 111.
  • Edge: 111.
  • Firefox: 113.
  • Safari: 9.

Examples

For example, :nth-child(2 of .highlight) selects the second matching element that has the .highlight class. Put differently: out of all children with the class .highlight, select the second one.

This in contrast to .highlight:nth-child(2) which selects the element that has the class .highlight and also is the second child.

In the demo below you can see this difference:

  • The element that matches :nth-child(2 of .highlight) has a pink outline.
  • The element that matches .highlight:nth-child(2) has a green outline.

Note that S is a selector list which means it accepts multiple selectors separated by a comma. For example, :nth-child(4 of .highlight, .sale) selects the fourth element that is either .highlight or .sale from a set of siblings.

In the demo below, the element that matches :nth-child(4 of .highlight, .sale) has an orange outline applied to it.

Zebra-striping, revisited

A classic example where :nth-child() is used, is when creating a zebra-striped table. It’s a visual technique in which each table row alternates colors. Normally, this would be approached as follows:

tr:nth-child(even) {
  background-color: lightgrey;
}

While this works fine for static tables, it becomes problematic when you start to dynamically filter the table contents. When, for example, row two gets hidden, you’d end up with rows one and three visible, each with the same background color.

To fix this, we can leverage :nth-child(An+B [of S]?) by excluding the hidden rows from the An+B logic:

tr:nth-child(even of :not([hidden])) {
  background-color: lightgrey;
}

Pretty sweet, right?

Photo by Markus Spiske on Unsplash