CSS Parent Selectors
Tags: css, webdev, tutorial
Published: 2022-07-02 12:49:48

CSS stands for **cascading stylesheets**, which basically means things later on in the page take precedence over things earlier (with some [major caveats](https://fjolt.com/article/css-selectors-guide])). 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](https://fjolt.com/article/css-selectors-level-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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/28sgwm0mqtca2yyogib6.png) 

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](https://www.npmjs.com/package/css-has-pseudo) 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:
```css
div p {
    color: red;
}
```

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:

```css
/* Makes the div color: red; */
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:
```css
div:has(> span) {
    color: red;
}
```

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`:
```css
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:
```css
div:not(:has(p)) {
    color: red;
}
```

## 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:

```css

div:not(:has(*)) {
    background: green;
}
```

## 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](https://www.npmjs.com/package/css-has-pseudo), 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](https://fjolt.com/category/css).