top of page

What is CSS :has() Selector?

CSS is constantly evolving, and new features emerge to empower developers to create more efficient and visually stunning web experiences. One such recent addition is the CSS :has() selector, a game-changer for targeting elements based on their child content.


CSS has selector

This article explores the CSS :has() selector, its functionalities, benefits, and practical applications. We'll learn how it simplifies styling, improves code maintainability, and unlocks possibilities for dynamic styles.


What is CSS :has() Selector?

The CSS :has() selector is a relatively new pseudo-class introduced in CSS Selectors Level 4 to target elements based on the presence of their child elements matching a specific selector.


Browser Support (as of March 2024):

  • Well Supported: Chrome and Safari have full support for CSS :has().

  • Evolving Support: Other browsers like Firefox are catching up with experimental flag support.


Things to Consider:

  • Newer Feature: While gaining traction, :has() might require polyfills or workarounds for older browsers.

  • Performance: Extensive use of CSS :has() with complex selectors could slightly impact performance.

  • Readability: Complex :has() statements can be hard to read and understand.


Syntax

The syntax for the CSS :has() selector is:

.parent:has( .child-selector ) {
  /* styles for the parent element */
}

In this code:

  • .parent: This is the selector targeting the parent element you want to style. You can use any valid CSS selector here, like IDs or class names.

  • :has(.child-selector): This is the pseudo-class that checks for a child element matching the .child-selector. The colon (:) precedes the keyword "has" followed by parentheses containing the selector for the child element.


The styles within the curly braces ({ }) will only be applied if the parent element has a child element that matches the condition specified by the .child-selector.


Note:

You can target multiple child elements within the CSS :has() by separating them with commas. The CSS :has() selector pseudo-class can also target elements by tag name, attribute selectors, or other valid selectors.


Benefits and use cases

  • Improved Specificity: CSS :has() promotes cleaner and more specific CSS by targeting elements based on their content. This reduces the need for nested selectors and extra classes on parent elements solely for styling purposes.

  • Dynamic Styling: It unlocks the potential for dynamic styles. The styles applied can adapt and change based on the actual content structure of your HTML.

  • Reduced Coupling: By styling parents based on child content, you decouple styles from the specific HTML structure. This can be beneficial for reusable components or situations where the HTML structure might change in the future.


Functionality:

1. Targeting by Child Content with CSS :has()

Traditionally, CSS offered limited ways to target elements based on their child content. Here's how the new CSS :has() selector changes the game:


Traditional Methods:

Nested Selectors: This involved nesting selectors to target child elements within a parent. While functional, it can lead to complex and difficult-to-maintain code, especially for deeply nested structures.

/* Target headings (h2) within a section with class "content" */ 
.content h2 
{ 
	/* styles for h2 headings within the section */ 
}

Adjacent Sibling Selector (~): This could be used for specific scenarios where you want to style an element based on its immediate sibling's content. However, it's limited to targeting direct siblings and not deeper nested elements.

/* Target paragraphs (p) following an image (img) */ 
img ~ p 
{ 
	/* styles for paragraphs following an image */ 
}

Here's where the CSS :has() selector comes in. It flips the script by allowing you to target parent elements based on whether they have specific child elements matching a selector list.


Example with CSS :has()

figure:has(figcaption) {
  /* styles for figures with captions (figcaption child) */
  border: 1px solid #ddd;
  padding: 10px;
}

figure:not(:has(figcaption)) {
  /* styles for figures without captions */
  border: none;
}

In this example, the first rule targets all <figure> elements that have a child element matching the selector figcaption. This means the figure will only be styled with a border and padding if it has a <figcaption> element as its child.


The second rule uses CSS :not(:has(figcaption)) to target figures that do not have a child element matching figcaption. This allows you to style figures without captions differently, perhaps removing the border altogether.


Benefits of Targeting by Child Content:

  • Improved Specificity: CSS :has() helps you write more specific CSS by targeting elements based on their content. This reduces the need for complex nested selectors (like targeting the figcaption element within the figure) and avoids adding unnecessary classes to parent elements just for styling purposes.

  • Cleaner Code: By separating style rules based on child content, your CSS becomes more maintainable and easier to understand.


2. Functional Pseudo-Class with Argument

The CSS :has() in its pseudo-class nature. Unlike regular selectors targeting elements directly, :has() is a functional pseudo-class. This means it acts like a function within your CSS code, taking a list of selectors within parentheses (). This list specifies the child elements you want the parent to have.


The Syntax:

/* Target parent (.parent) that has a child element matching (.child-selector) */
.parent:has(.child-selector) {
  /* styles for the parent element */
}
  • .parent: This is the selector targeting the parent element you want to style. It can be any valid CSS selector, like a class name or element tag.

  • :has(.child-selector): This is the pseudo-class that checks for a child element matching the .child-selector. The colon (:) precedes the keyword "has" followed by parentheses containing the selector for the child element.


Example with Different Child Selectors:

/* Target menus with an "ul" child (unordered list) */
nav:has(ul) {
  background-color: #eee;
}

/* Target articles with an "h2" heading child */
article:has(h2) {
  border-bottom: 1px solid #ccc;
}

In these examples, :has() accepts different child selectors within the parentheses. You can target elements based on class names, element tags, or even a combination of both using commas to separate them.


Benefits of Functional Pseudo-Class:

  • Flexibility: The ability to specify a list of selectors within CSS :has() allows for great flexibility in targeting parent elements based on various child content conditions.

  • Dynamic Styling: This opens the door to creating dynamic styles that adapt based on the actual content structure of your HTML.


Comparing CSS Pseudo-class: CSS :has() vs :not()

Both CSS :has() and :not() are functional pseudo-classes in CSS that offer unique functionalities for targeting elements. Here's a breakdown of their differences and how they compare:

Factors

CSS :has()

CSS :not()

Targeting Logic

Targets parent elements based on the presence of specific child elements matching a selector list. It essentially checks if a parent element "has" a child that fulfills a certain criteria.

Targets elements that do not match a specific selector or another pseudo-class. It essentially excludes elements based on a condition.

Focus

CSS :has() focuses on the parent element, applying styles based on the presence or absence of specific child elements

:not() focuses on the element itself, excluding elements based on a condition.

Specificity

CSS :has() can potentially lead to more specific selectors when targeting parent elements based on child content.

:not() might be less specific depending on the complexity of the excluded condition.

Example

ul:has(li:not(.active)) { /* styles for unordered lists (ul) that have list items (li) without class "active" */ list-style: disc; /* Example style for the ul */ }

li:not(.active) { /* styles for all list items except those with class "active" */ color: black; }

When to use

When you want to style parent elements based on the presence or absence of specific child elements. This can lead to cleaner and more maintainable code.

When you want to exclude elements from a style rule based on a specific condition. This is useful for cases like styling all elements except those with a particular class.


DO's for using :has() to style parent elements:

  • Improved Specificity and Maintainability: CSS :has() allows you to target parent elements based on their child content, leading to more specific and maintainable code. You can avoid complex nested selectors or adding unnecessary classes to parents just for styling purposes.

  • Dynamic Styling: CSS :has() enables you to create dynamic styles based on the content within an element. This allows for more visually interesting and responsive designs.

  • Reduced Coupling: By styling parents based on child content, you can potentially decouple the styles from the specific HTML structure. This can be beneficial for reusable components or situations where the structure might change.


DONT's for using :has() to style parent elements:

  • Browser Support: As discussed earlier, CSS :has() is a relatively new feature with evolving support. While major browsers like Chrome and Safari are on board, others might require polyfills or workarounds for older versions.

  • Performance: CSS :has() can potentially impact performance in some cases, especially when used extensively or with complex selectors. The browser needs to check for child elements, which might be slightly slower than traditional selectors.

  • Readability: While CSS :has() can be clear, complex :has() statements with nested selectors might become hard to read and understand for other developers.


Here are some additional points to consider:

  • Alternatives: Depending on the specific situation, there might be alternative solutions using existing selectors or pre-processing tools (like SASS) that could achieve similar results without relying on CSS :has() selector.

  • Progressive Enhancement: You can strategically use CSS :has() for modern browsers while including fallback styles for older browsers to ensure a good user experience across platforms.


Conclusion

The new CSS :has() selector that lets you style elements based on their content. It's like having a superpower for cleaner, more flexible styles! This means you can target parent elements depending on what's inside them, ditching complex nested selectors.

bottom of page