The Problem
One thing that bothers me with CSS :nth-of-type
/:nth-child
selectors is that you can’t combine them with CSS classes. The selector below for example won’t work as you’d first expect it to work:
.bar:nth-child(2) {
color: red;
}
No, the selector above won’t color the second .bar
element red
, something I had somehow expected it to do. What the selector will target instead, is an element that is both .bar
and :nth-child(2)
:
See the Pen .class:nth-of-type by Bramus (@bramus) on CodePen.
~
Enter :nth-child(An+B [of S]?)
To solve my problem, I’d be needing something like a :nth-of-class
selector. Unfortunately no such selector exists nor is it currently proposed. What is proposed though, in the upcoming CSS Level 4 Selectors specification (aka “not CSS4” 😜), is an extension to the nth-child/nth-last-child
pseudo selectors: the ability to add an optional of S
part into those selectors.
The
:nth-child(An+B [of S]?)
pseudo-class notation represents elements that are amongAn+B
th elements from the list composed of their inclusive siblings that match the selector listS
, which is a<complex-real-selector-list>
. IfS
is omitted, it defaults to*|*
.
Thanks to this of S
extension, we can select all children that also match the given selector-list S
. By filling in the An+B
clause, we can then select the An+B
th from that pre-filtered set of children.
All together, our wanted :nth-of-class
selector becomes a reality:
:nth-child(2 of .bar) {
color: red;
}
In words, the selector reads: “Within its parent, select all article
children that are also matched by .bar
. From that prefiltered set, give me the second item.”
💡 Note that the .bar
selector used here is pretty simply as a value for S
. S
accepts a comma-separated list of <complex-real-selector>
s which roughly translates to just about any selector as long as it’s not a relative one or one that targets a pseudo-element. That means that, for example, :not(.foo)
and section > .bar
are also allowed values for S
.
~
Demo
Here’s a pen with the result:
See the Pen .class:nth-of-type by Bramus (@bramus) on CodePen.
~
Browser Support
If you’re using Firefox or Chrome you’ll see that the demo above is broken, as browser support for it is bad. Only Safari supports it at the time of writing.
UPDATE 2023.01.01 Chrome Canary 111.0.5530.0 comes with experimental support for :nth-child(An+B [of S]?)
. To try it, flip on the “Experimental Web Platform Features” feature flag through chrome://flags
Other browser that currently don’t support it have meta-issues to implement CSS Level 4 selectors:
- Chromium Bug #747818: [meta] CSS Selectors Level 4 support
- Bugzilla Bug #693083: [META] implement CSS Selectors Level 4 [selectors-4]
Please vote on those bugs mentioned above if you want those features to land in the browsers.
🗳 By massively voting on browser bugs we can “tell” a browser vendor that we’d like to see a certain feature land. Go forth and vote.
~
Thank me with a coffee.
I don\'t do this for profit but a small one-time donation would surely put a smile on my face. Thanks!
To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.
Hi,
Did you try article[class=”bar”]:nth-of-type(2)? it works in your example
That still selects the first
.bar
(as it happens to also be the 2nd element in the list)This is a workaround but since lists or rows are usually rendered using .map or by iterating, I added ‘even’ and ‘odd’ class names to every other element and then can target them in css without :nth-child.
pseudo-react example:
DIV className={‘my-class ‘ + (i%2 === 0 ? ‘even’ : ‘odd’)} /DIV
.my-class.odd {
background: grey
}
Happened upon this article and just wanted to share my alternative solution:
.bar + .bar:nth-child(2n) {
background: grey;
}
That won’t work unfortunately as:
1. You’ll have to adjust the offset (2n should become 1n in that case)
2. You’ll get successive hits for all even (2n) matches.
See https://codepen.io/bramus/pen/da8eebb9189eb2e26a02f45d2d159225 for a demo of this wrong behavior.
It is now well supported (baseline 2023)!