When and how to use :has()

Table of contents

2023 should had been the :has() year, a function that lets us evaluate whether a selector “has another element”, very useful for applying styles to an element depending on its content, something similar to the parent selector that never arrived (and won’t), but we had to wait until the end of the year to use it widely, since Firefox started supporting this function. CanIUse

Until then, we could use it for small decorating details, not big deal, and we had to assume that some users wouldn’t see it, or we could supply a fallback using javascript, which lead us adding more code, making it more complex (probably not needed) increasing the risk of failing, because coding irremediable opens the possibility to make errors, doesn’t it?

So this article came a year later but more mature.

Use cases

:has() accepts a list of relative selectors (<relative-selector-list>) as argument and checks if at least one of them matches the DOM. If the condition is met.

The simple example would be p:has(> a) where it evaluates whether a paragraph contains a link. The difference with p > a is that in the first case we are selecting the p while in the second case the styles are applied to a a which is the selected element. That is why we say that we can apply styles conditionally based on its content or that it is like a parent selector.

But it is something else.

As the selector is relative, the argument makes relation to the element that evaluates (the one that uses it), but we may want to apply styles to a sibling element depending on if the previous (or parent) has some particularity, for example: section:has(.box) + section.

From here in beyond, you can try the weirdest queries you can think of, some will work and some won’t or not at first, the ones that involve all the pseudo-classes of the nth-of-type() are especially difficult, but some of the most common ones are, for example:

Check if an element contains another element

Evaluate whether an element contains another element, class or attribute.

article:has(h1, h2, h3, h4, h5, h6)
section:has([class^="item"]) li[class]

Check if an element does NOT contain another element

We can also evaluate whether an element doesn’t contain another element, class or attribute.

figure:not(figcaption)

Be careful when combining it with :not() because the order of declaration alters the sense of the expression, it is not the same li:not(:has(span)) than li:has(:not(span)) because it is not the same that they do not contain span than that they contain anything that is not a span.

Children count

We can evaluate the number of children a node has to apply a flex or grid type solution.

ul {
  display: flex;
}

ul:has(> :nth-child(5)) {
  display: grid;
  grid-template-columns: 1fr 1fr;
}

Check open elements

As we can evaluate classes or attributes, it is an ideal solution to block page scrolling when a popup element is open.

body:has(.is-modal-open) {
  overflow-y: hidden;
}

body:has(dialog[open], details[open]) {
  overflow-y: hidden;
}

body:has([aria-expanded="true"]) {
  overflow-y: hidden;
}

Particularities

Of course, it’s not all partying, there are some limitations.

Aladdin's genie numbering his eight fingers

:has() can’t be nested, so selectors of this style won’t work.

body:has(section:has(span)) {
	outline: 10px solid lime;
	outline-offset: -12px;
}

:has() can’t evaluate pseudo-elements (those elements are virtual) because can’t be found in the DOM.

body:has(::before) {
	color: #f06;
}

Demo

Here is a small demo so you can try the above explained.

Know more

The combinations of :has() are many, so many that you will rarely need to use them because you could solve some situations with other tools or approaches, but it is convenient to know how much you can stress them. To go further, I can’t think of a better place bram.us where you’ll find a large collection of related articles, where the :has() selector is pushed to its limits.

Resources

comments powered by Disqus

If you find it interesting

If you have any doubt or you want to talk about this topic, if the content or our profiles are interesting to you and you think we could something together, do not hesitate to contact us on twitter or trough the email address hola@mamutlove.com