CSS :has() unleashed

CSS has()

The CSS :has() selector was introduced in December 2023 and enables developers to apply styles by determining whether a parent element has a given child or whether that child is in a specific condition.

It is a powerful tool that allows us to select elements based on the presence of other elements within them. Unlike traditional parent selectors, which have limited flexibility, the :has() selector provides a more dynamic and versatile way to target specific elements within a document's structure.

Fully Supported

CSS has()

Image from Mozzila website: https://developer.mozilla.org/en-US/docs/Web/CSS/:has

All of the main browsers now support this feature. We can use several CSS combinators to select the parent element and other elements by passing in a relative selector list as input to the :has() selector. With the help of this powerful new feature, we may now do feats on the web platform that were unthinkable for CSS and UI before.

Samples

Here's an example of how the :has() selector works:

div:has(p) {
  background-color: yellow;
}

In this code snippet, we select all div elements containing a p element and apply a yellow background color. This means any div containing a p element will change its background color to yellow.

You can also use the :has() selector to select elements based on the presence of multiple elements, like this:

div:has(p, span) {
  background-color: yellow;
}

In this case, all div elements containing either a p or a span element will receive the yellow background color.

Versatile

Here are some more examples demonstrating the versatility of the :has() selector:

a) Select all elements that contain an IMG element:

a:has(> img) {
  border: 1px solid red;
}

This code applies a red border to all elements that contain an IMG element.

b) Select all li elements that contain an element with a target attribute:

li:has(a[target]) {
  background-color: yellow;
}

In this example, any li element with a child element with a target attribute will have a yellow background color.

c) Select all h2 elements that are followed by an IMG element:

h2:has(+ img) {
  margin-bottom: 20px;
}

Here, h2 elements followed by an img element will have a margin-bottom of 20px applied.

d) Select all div elements that contain a p element with a class of warning:

div:has(p.warning) {
  background-color: yellow;
}

This code targets div elements containing a p element with the class warning and changes their background color to yellow.

e) Select all elements that contain an IMG element with an alt attribute containing the word "logo":

a:has(> img[alt*="logo"]) {
  border: 1px solid red;
}

In this example, an element with an IMG child element whose alt attribute contains the word "logo" will have a red border.

f) Select all li elements that contain an element with a target attribute, excluding the first child of their parent:

li:not(:first-child):has(a[target]) {
  background-color: yellow;
}

This code targets li elements that are not their parent's first child and contains an element with a target attribute, applying a yellow background color to them.

g) Select all h2 elements followed by an IMG element with a class attribute of thumbnail:

h2:has(+ img.thumbnail) {
  margin-bottom: 20px;
}

This example selects h2 elements followed by an IMG element with a class attribute of thumbnail and adds a margin-bottom of 20px.

Limitations

Not ALL existing browsers fully support it, which is one of its key drawbacks. For instance, the defunct Internet Explorer does not support the :has() selector. Another drawback is its inability to choose elements based solely on their parents or siblings. It can also only select items that are offspring of the parent element.

It's crucial to remember that JavaScript cannot be replaced by the :has() selector. It works well for straightforward layout and design. You should utilize JavaScript instead if you require more sophisticated capabilities. And we can't hack NASA with :has() :).

Performance

The :has() selector performs less efficiently and with lesser speed than other CSS selectors. This is because to locate the matching items, the browser must navigate the DOM tree. On the other hand, some selectors are quicker since they can match directly against the element's properties, such as the class and ID selectors.

Note that in small-scale applications, there could not be much of a performance difference between the :has() selection and other selectors. Nonetheless, the :has() selector may have a major performance impact in larger applications with intricate DOM structures. It is therefore advised to utilize the :has() selector sparingly and only when required.

It is noteworthy that the performance bottleneck in web performance is not the CSS selector. Many other aspects, like as fast CDNs, caching, image sprites, and optimizing your JavaScript and connection speed, have a significantly bigger influence on web performance. 

Conclusion

The :has() selector is a valuable addition to CSS, allowing for more precise and targeted styling based on the structure and content of HTML documents.

It gives us greater control over our designs and enhances the flexibility of CSS selectors.

And about you? Tell me your experience with CSS and what you think about :has() to create killer apps.