Selecting parent elements has become easier
Tags: webdev, beginners, tutorial, css
Published: 2022-10-28 17:06:27

---
title: "Selecting parent elements has become easier"
subtitle: "unwrapping the :has selector"
tag: "CSS"
slug: "the-has-selector"
summary: "CSS has a lot of ways to select elements in the HTML and so many options can make it difficult to choose which method is most sufficient. The :has selector gives us another way to select parent elements but it allows us to do so much more. Here we deep dive into what the :has selector is and explore different use cases"
publishedDate: "2022/10/28"
updatedDate: "2022/10/28"
isPublished: true
---

CSS has a lot of ways to select elements in HTML and so many options can make it difficult to choose which method is most sufficient. This becomes very apparent when we have multiple elements on the pages that share styles but one of them, in particular, has a different style than the rest.

The traditional way to solve this problem is to place another CSS class on the component that is going to get this **Special style**. It may look something like this

```html
<div class="card">...Some content goes here</div>
<div class="card special-card">
  ...Some content goes here
  <p class="card__subTitle">Some more content for the special class</p>
</div>
<div class="card">...Some content goes here</div>
```

The middle `div` element has two CSS classes: 1) one denoting that it will get all the styles in the card class and 2) denoting that it will get all the styles from the special-card class.

There hasn't really been a way to select an element with CSS if it has certain descendants -- at least not until recently.

## The HAS Selector

The `:has` selector also comically known as the parent selector is a pseudo-class that was introduced in the CSS spec. `:has` allows for the selection of an element if the arguments passed into `:has` match at least one element when anchored against the parent element. Essentially it allows for the selection of an element based on the presence or absence of descendant elements.

Revisiting our example above, let's say our special card had a paragraph that implemend its own padding in the in-line direction. Since we want all the cards to have the same amount of padding we need some way of addressing this problem. We can reach for the solution above, or we can implement the `:has` selector. We can select the card if it has a paragraph as its child and then set the in-line padding on the card to 0. It looks something like this:

```css
.card:has(.card__subTitle) {
  padding-inline: 0;
}
```

The above code block translates to if an element with the class of `.card` has a descendant with the class of `.card__subTitle` then set the in-line padding on the parent element (in this case the element with the class of `.card`) to 0.

```html
<div class="card">...Some content goes here</div>
<div class="card special-card">
  ...Some content goes here
  <p class="Title">Some more content for the special class</p>
</div>
<div class="card">...Some content goes here</div>
```

### Multiple selectors inside `:has`

The cool thing is that we aren't just limited to putting one item inside the `:has` pseudo-class. We can chain on selectors and get really specific as to when we want this `parent element` to be selected. Chaining on selectors looks something like this:

```css
.card:has(.card__subTitle, .Title) {
  padding-inline: 0;
}
```

The above code block translates to if an element with the class of `.card` has a descendant with the class of `.card__subTitle` **or** `.Title` then set the in-line padding on the parent element (in this case the element with the class of `.card`) to 0.

You might have noticed the keyword **OR** above. This is important and a key differentiator between `:has` and other selectors. Usually with CSS if you are chaining selectors, if one of the selectors in your chain does not match, then the entire rule is skipped. This is not the case with the `:has` pseudo-class, and it is something that differentiates it from other selectors. As long as there is one matching selector inside the `:has` pseudo-class, then the parent element will be selected.

You might be wondering how we may go about saying **AND** with this selector. More specifically how do we go about saying the following: Select the element with the `.card` class if its descendants have elements with the `.first` **and** `.second`classes. Using the scenario above `.card:has(.first .second)` matches at least one. What happens if we require both to be present?

We can attach another `:has` pseudo-class. It would look something like this:

```css
.card:has(.first):has(.second) {
  padding-inline: 0;
}
```

The above code block translates to if an element with the class of `.card` has a descendant with the class of `.first` **and** `.second` then set the in-line padding on the parent element (in this case the element with the class of `.card`) to 0.

The other cool thing about `:has` is that we can chain complex selectors when the `parent element` is to be selected. We can say something like this:

```css
.card:has(> .first .title) {
  /* Some css rule */
}
```

Only select an element with a `.card` class if it has direct decedents with a class of `.first` which has a descendent with a class of `.title`.

### More than just a parent Selector

The `:has` pseudo-class is more than just a parent selector because we can use it to select other elements that are in relation to the parent element. The following describes how we are using the `:has` pseudo-class to select a paragraph element.

```css
.card:has(.subTitle) > p {
  /*Some Css Rule */
}
```

The above code block translates to: select any paragraph element that is a direct decedent of `.card` if the `.card` has a descendant element with the `.subTitle` class. Putting this into practice, below we can see that the manager's name is in **bold** while the staff's name is not. This is being done with a rule similar to the one we described above.

## Specificity

Calculating specificity with the `:has` pseudo-class gets a bit complicated and so we are going to walk through an explanation and then some examples.

Let's say we have the following scenario:

```css
.card:has(.first p) {
  /* Some css rule */
}
```

What is the specificity here? We first look at the selector outside the `:has` pseudo-class and then focus on the selectors inside the `:has` pseudo-class. Here we have `1 class` selector outside `:has` and then `1 class selector` & `1 element selector` inside `:has`. Therefore, we say that the specificity of the entire selector is `2 class selectors` & `1 element Selector`.

The interesting thing is that calculating specificity changes when there are multiple selectors inside the `:has` pseudo-class that are separated by a comma. In this case `:has` will only use the specificity of the most specific selector. This is demonstrated below:

```css
.card:has(.first, p) {
  /* Some css rule */
}
```

What is the specificity here? Remember, we first look at the selector outside the `:has` pseudo-class and then focus on the selectors inside the `:has` pseudo-class. Here we have `1 class` selector outside `:has` & `1 class selector` & `1 element selector` inside `:has`. Since the **class selector** and the ** element selector** are separated by a comma, we look at the selector with the highest specificity. In this case, it would be the class selector. Therefore, we say that the overall specificity is `2 class selectors`.

In the case where we have multiple `:has` pseudo-classes, we calculate the specificity for the individual `:has` and then combine them all to get an overall specificity. The following is an example of this:

```css
.card:has(.first):has(.second p) {
  padding-inline: 0;
}
```

Here we have `1 class` selector outside `:has`, `1 class selector` inside the first `:has`, and `1 class selector` & `1 element selector` inside the second `:has`. These specificities are all combined to give us an overall specificity of **3 classes** and **1 element**.

## Browser support

According to [Can I use](https://caniuse.com/) as of the time of writing this article the `:has` pseudo-class is at 76% support in terms of browsers. Moreover, it is supported in the latest version of Chromium & Safari, and it is under an experimental flag in firefox (thus I expect it to be fully supported in firefox fairly soon).

## Conclusion

This `:has` selector is an incredible selector and it gives us another way to author the CSS for our applications. I can't wait for it to be fully supported across all major browsers!

Alight I'll catch you in the next one -> peace out!