In the CSS Selectors 4 specification, CSS introduces a new selector called :has()
, which finally lets us select parents. That means is we’ll be able to target a CSS element that has specific children within it. This is already supported in Safari and is also in Chrome 105. The full support table is shown
here.
Parent Selectors workings
In CSS, if we want to select something, we use selectors that descend the DOM.
For example, selecting a p tag within a div tag looks like this:
div p {
color: red;
}
Until now, couldn’t really select the div
tags which had p
tags within them, though, and this meant we had to resort to Javascript
. The main reason this wasn’t implemented in CSS is that it’s quite an expensive operation to do. CSS is relatively fast to parse, but selecting parent tags requires a relatively significantly larger amount of processing.
Using the :has
selector, we can now select div
elements which have a p
children, or any normal combination of selectors.
For example, selecting a div
with a child p
now looks like this:
div:has(p) {
color: red;
}
This will make any div
with a child p
red.
Combining parent selection with other selectors
Just like any other CSS selector, we can combine this for specific circumstances.
For example, if you want to select only div
tags which have direct span
children:
div:has(> span) {
color: red;
}
As the vocabulary of :has
suggested, it is not just limited to parent selection.
For example, below we can select a span
which :has
a sibling div
:
span:has(+ div) {
color: red;
}
Or even, selecting an element which does not have a child, by using the :not() selector.
For example, the following will select any div which does not have a p child:
div:not(:has(p)) {
color: red;
}
Selecting elements that only contain text in CSS
One very common problem in CSS is that the :empty
tag does not select elements that contain any text - so sometimes an element can contain one space, and :empty
will not apply. The :has
selector gives us the power to select elements that only contain text nodes and no other child elements.
Although this is not the perfect solution for simply :empty
elements with spaces (as this will select any element with just text and no additional HTML DOM elements) - it does give us the ability to select DOM elements with only text nodes, which was not previously possible. We can achieve this with the following code:
div:not(:has(*)) {
background: green;
}