THE :HAS SELECTOR

3 min read1st November 2022#CSS #frontend #parent-selector

On June 18th 2009 jcuenod asked 'Is there a CSS parent selector?' on stackoverflow. Back then and for the longest time, the answer to his question was simply 'no, there isn't!'. However, I would argue that every developer working with CSS for a while has run into the problem to style a parent element based on one of its children. The fact that this question on stackoverflow has been viewed over 3 million times by the time of this writing, supports this statement.

Finally the :has selector has arrived, as it is part of the CSS Selectors Level 4 Specification! The :has selector is a pseudo-class (like :hover, not to be confused with a pseudo-element like ::after or ::placeholder).

Let's say you have the following HTML:

<div>
  <p>This is text</p>
</div>

<div>
  <a href="https://schwesi.com">This is a link</a>
</div>

Now, let's assume we would like to give the text a border. The old fashioned way to do this would be to add a class to the first div, something like this:

<div class="border">
  <p>This is text</p>
</div>

<div>
  <a href="https://schwesi.com">This is a link</a>
</div>

<style>
  .border {
    border: 2px groove red;
  }
</style>

This works, however we have to add extra syntax to our HTML and complicate the entire code base, especially if we are doing this multiple times.

So let's use some magic:

<div> <!-- SELECTED -->
  <p>This is text</p>
</div>

<div> <!-- NOT SELECTED -->
  <a href="https://schwesi.com">This is a link</a>
</div>

<style>
  div:has(p) {
    border: 2px groove red;
  }
</style>

In the above code snippet, we only select divs with a paragraph as a child, in this case the first div. Therefore we do not need to add any extra syntax to our HTML.

That is all great fun, however you can become much more creative with this selector. Some of the examples I found researching this article include:

This snippet found at https://drafts.csswg.org/selectors-4/#relational, makes clever use of the :not selector. All section tags are matched, which do not include a heading tag:

section:not(:has(h1, h2, h3, h4, h5, h6))

This amazing snippet found at https://blog.webdevsimplified.com/2022-09/css-has-selector/ selects any heading element that has a direct child with the class of subtitle which contains a strong element in it with the id of accent:

.heading:has(> .subtitle strong#accent)

At the time of writing every major browser, except Firefox supports the :has selector.

I consider the :has selector to be a game changer. After so many years I did not except it to become a reality anymore. Using it just feels like magic!

Sources