The :has()
relational pseudo-class is officially recognized as a Parent Selector, and in the actual spec [Selectors Level 4], it is described as a relative selector that accepts “forgiving-relative-selector-list“. However, :has()
is extremely capable as far as selectors go, and there’s plenty of proof that it is more than just a parent selector.
Syntax
The syntax of the :has()
pseudo-class is relatively simple and easy to learn to get started, because its semantics are very clear and consistent with the rendering presentation, meaning that the so-and-so element will match if it matches the so-and-so selector.
As a simple example, the following function means that if the element has an <a>
element inside the <img>
element, the <a>
element will match.
a:has(img) {
display: block;
}
We can use this selector to easily distinguish whether it is a text link or an image link and set different CSS styles.
The :has()
pseudo-class supports all CSS selectors. E.g.:
a:has(> img) {
display: block;
}
The above function indicates that a
elements whose matching child element is an img
element will be matched, while more distantly related descendant elements will not be considered.
Note that in the above code, the :has
pseudo-class argument - >
selector - is written directly at the top of the argument, rather than a:has(a > img)
. The :has()
pseudo-class has an invisible :scope
pseudo-class at the top of its argument, so it is prohibited to write a:has(a > img)
.
Similarly, you can use selectors like +
or ~
to achieve the effect of a "front sibling selector", where the front element is selected based on the back sibling element. Like so:
h3:has(+ p) {
font-size: 1.5rem;
}
The above query indicates a match for an h3
element followed by an p
element. The :has()
pseudo-class also supports complex selectors and selector lists. E.g.:
article:has(h3, p) {
background-color: #f3f3f3;
}
The above query indicates that only h3
elements or p
elements inside the article elements will match: as an or relationship, not a with relationship.
If you want the relationship to be with, i.e., both h3
elements and p
elements to match, you can write it like so:
article:has(h3):has(p) {
background-color: #f3f3f3;
}
The :has()
pseudo-class can also be mixed with other pseudo-classes, such as :checked
, :enabled
, etc., or even with other logical pseudo-classes, such as :not
. E.g.:
section:not(:has(h3)) {
border: 1px solid yellow;
}
section:has(:not(h3)) {
color: orange;
}
Note that the two queries above have different meanings.
The former selector means that section
elements that do not contain h3
elements have a yellow border, while the latter means that section
elements that contain elements that are not h3
elements are orange in color.
It is honestly confusing for everyone. But once you understand that not -> has means has not, it gets easier.
Examples
The :has()
pseudo-class has been available for quite some time now (either under browser flags or with a polyfill), and the possibilities of what this parent selector will be able to do will have to be unraveled over time.
In the meantime, this section covers examples of creative ways in which :has()
is already being utilized.
#1: Styling figcaption
only if it is present
#2: Carousel gallery with scale and gap effect in pure CSS
#3: Creating a native color theme switcher
Browser Support