Updated 2022 CSS Selectors 4
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;
}