CSS Parent Selectors

Johnny Simpson - Jul 2 '22 - - Dev Community

CSS stands for cascading stylesheets, which basically means things later on in the page take precedence over things earlier (with some major caveats). This also applies to how we select elements - from parent to child, with no way to select parents, until now.

In the CSS Selectors 4 specification, CSS introduces a new selector called :has(), which finally lets us select parents. What that means is we'll be able to target a CSS element which has specific children within it. This is already supported in Safari, and is also in Chrome 105. The full support table is shown below:

CSS Parent Selector Support

With support increasing, in this article I will focus on how CSS parent selection works, and how you can do it today where support is available. In the meantime, if you require full support in all browsers, you can also implement this polyfill until native CSS support is available.

How Parent Selectors work in CSS

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;
}
Enter fullscreen mode Exit fullscreen mode

Until now, we 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 because it's quite an expensive operation to do. CSS is relatively fast to parse, but selecting parent tags requires a relatively significant 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:

/* Makes the div color: red; */
div:has(p) {
    color: red;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

As the vocabulary of :has suggests, 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;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

Selecting elements which only contain text in CSS

One very common problem in CSS is that the :empty tag does not select elements which 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 which 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;
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

With the addition of :has() selector support in Chrome 105, parent selection is quickly becoming a reality that we will soon be able to use on real life projects. As of now, with Safari support, it's easy to test and see how it will work in the future. This has the additional benefit of letting us cut back on Javascript solutions to parent selection which is quite common in many applications and products.

You can even start using :has today, if you also implement this polyfill, and then remove the polyfill once native support comes to Chrome and other browsers.

I hope you've enjoyed this article. To learn more about CSS, click here.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .