Safari Technology Preview 137 just dropped, with unflagged support for CSS :has()
. Often dubbed “the parent selector”, CSS :has()
is way more than that …
~
# CSS :has()
?
As per selectors-4 specification:
The
:has()
CSS pseudo-class represents an element if any of the selectors passed as parameters match at least one element.
This selector is dubbed “the parent selector”, as the default cases indeed allow you to select a parent element that has certain children.
/* Matches <a> elements that contain an <img> child */
a:has(img) { … }
/* Matches <a> elements that directly contain an <img> child */
a:has(> img) { … }
/* Matches <section> elements that don’t contain any heading elements: */
section:not(:has(h1, h2, h3, h4, h5, h6))
Cool!
~
# More than a parent selector
The :has()
selector is way more than that just a parent selector though. As Adrian Bece shared in his post on Smashing Magazine:
/* Matches <figure> elements that have a <figcaption> as a child element */
figure:has(figcaption) { … }
/* Matches <img> elements that is a child of a <figure> that contains a <figcaption> child element */
figure:has(figcaption) img { … }
The figure:has(figcaption)
selector matches the figure
that has a figcaption
. The figure:has(figcaption) img
selector OTOH matches the img
inside of the figure
that has as figcaption
Here’s a pen:
See the Pen The CSS :has() selector is way more than a “Parent Selector” by Bramus (@bramus)on CodePen.
In browsers that support :has()
you should see a red dashed border around the top image.
Here’s another example from the spec which uses a relative selector. It selects the h1
that is followed by a p
, showing that selection is not limited to parents only.
/* Matches <h1> elements only if they have a <p> element directly following them */
h1:has(+ p) { … }
This works because the :has()
relational pseudo-class selector accepts a <relative-selector-list>
as an argument. That’s a list of <relative-selector>
s which can contain any of the combinators we already know: +
, ~
, >
, …
UPDATE: My colleague Jhey has a nice article up on developer.chrome.com where he calls it “The Family Selector” – I like this name!
~
# Other peculiar traits
Just like CSS :is()
, CSS :has()
has these specific traits:
The selector list ofAs per CSSWG resolution on 2022.12.07, the argument now is a (non-forgiving):has()
is forgiving<relative-selector-list>
list – This to not break jQuery.- The specificity of
:has()
is that of its most specific argument
Hit the post on CSS :is()
for more info on this.
~
# Browser Support
💡 Although this post was originally published in December 2021, the section below is constantly being updated. Last update: Dec 19, 2023.
Here is an up-to-date list of browser support for the CSS :has()
selector:
- Chromium (Blink)
-
✅ Available in Chromium 105 and up.
Experimental support first appeared in Chromium 103.
- Firefox (Gecko)
-
✅ Available in Firefox 121 and up.
Experimental support first appeared in Firefox 103.
- Safari (WebKit)
-
✅ Available in Safari 15.4 and up.
Experimental support first appeared in Safari Technology Preview 137.
The pen embedded below will indicate if the browser you are currently using supports CSS :has()
or not:
See the Pen
CSS :has Selector Support test by Bramus (@bramus)
on CodePen.
To stay up-to-date regarding browser support, you can follow these tracking issues:
- WebKit/Safari: Issue #227702 — RESOLVED FIXED
- Blink/Chromium: Issue #669058 — Fixed (Closed)
- Gecko/Firefox: Issue #418039 — NEW
~
# Spread the word
To help spread the contents of this post, feel free to retweet its announcement tweet:
The CSS `:has()` selector is way more than a “Parent Selector”.
🏷 #css #selectors pic.twitter.com/cwpv7esjwb
— Bram.us (@bramusblog) December 21, 2021
~
🔥 Like what you see? Want to stay in the loop? Here's how:
However, hów :has pseudoselector impacts on code efficiency (processor and ram uasge)?
Everything is OK but the stones did not fit properly 🙂
What is the difference with two things:
div:has(p) {color: #000}
div p {color: #000}
Why should I use :has() selector?
– `div:has(p)` targets the div itself.
– `div p` targets the p elements inside the div.
This is a pretty critical difference that should be mentioned at the top
The “default” usage of
:has()
is explained right here: https://www.bram.us/2021/12/21/the-css-has-selector-is-way-more-than-a-parent-selector/#whatcss:has() – this is the thing I am second-most excited about in browserland. #1 being container queries
Thanks for the detailed write-up. Shared with my colleagues 🙂