It’s common for a table design to feature alternating row colors, which is pretty trivial to accomplish with CSS. But then if you need to hide some rows later, the striped background gets all messed up.

Or at least it used to. It turns out I completely slept on a cool CSS feature that makes this a non-issue.

The typical approach #

Say you have a standard table and want to make the row colors alternate. You’d use the :nth-child() pseudo-class (which selects child elements based on their index within a parent) on the table body’s <tr> elements to select every-other row.

css
tbody tr:nth-child(even) { background: #EEE; }

By using the even keyword as the selector’s argument, its logic becomes “look at all of the rows and select the even-numbered ones.”

I’m using the even keyword as the function argument here, but you could also be using the odd keyword or even the An+B notation. It works pretty well.

See the Pen Striped Table #1 by Darin (@dsenneff) on CodePen.

But what if you need to hide some rows later on, say as part of a filtering feature? Let’s hide some rows using the hidden attribute.

html
<tr>...</tr> <tr hidden>...</tr> <tr>...</tr> <tr hidden>...</tr>

The :nth-child(even) selector is still counting all even-numbered rows regardless if they’re visible or hidden. So we end up losing our nice alternating stripes.

See the Pen Striped Table #2 by Darin (@dsenneff) on CodePen.

The of <selector> syntax approach #

I discovered that there’s an additional of <selector> syntax for the :nth-child() selector that I wasn’t even aware of! This syntax acts as an additional filter in the selector argument.

To fix the row background issue, just combine it with the :not() selector:

css
tbody tr:nth-child(even of :not([hidden])) { background: #EEE; }

With this syntax added, the selector’s logic now looks like this:

  1. "First, look for all of the rows that are not hidden;"
  2. "Now, select the even-numbered ones from that group."

This allows the striped table background to work regardless of any hidden rows.

See the Pen Striped Table #3 by Darin (@dsenneff) on CodePen.

There’s a lot of cool opportunities that this syntax unlocks. For example, instead of looking for hidden rows you could be filtering for elements that have a certain class attached.

Conclusion #

I’m not sure how it slipped past me, but the of <selector> syntax became baseline (supported in all four major browsers) in May 2023, almost three years ago at the time I’m writing this! I guess the nice thing about that is that it’s most-likely evergreen in browsers at this point, so browser support should be very good.

The table row example I used here is pretty simple and basic, but it’s the sort of CSS issue that we’ve all probably come across at some point that forces us to spend more time on than should be needed. I’m happy that now there’s another tool to leverage for those use cases.