The CSS :has selector is a powerful tool for efficient web design. It allows you to select elements based on the presence or absence of a specific element within them.
With the :has selector, you can create complex and flexible layouts with minimal code. For example, you can use it to target elements that contain a specific child element, such as a button within a form.
The :has selector is particularly useful for responsive design, as it enables you to create layouts that adapt to different screen sizes and devices.
What is :has?
The :has selector is a pseudo class that works similarly to :hover and :focus, but it's more like :is and :not if you're familiar with those. It's used to select a parent element based on the existence of a specific selector or list of selectors inside it.
To use the :has selector, you need to pass a selector or list of selectors to it, which will be applied to the parent element. This can be a bit confusing, but an example helps clarify things. For instance, a simple selector can select any element with the class .heading that contains an element with the class .subtitle inside it.
The :has pseudo element allows us to check for the existence of elements inside other elements while still selecting the actual parent element that we care about. This is different from the normal way CSS works, where you can only select the last element in a list.
What is :has?
The :has selector is a pseudo class similar to :hover and :focus. It works similarly to the :is and :not pseudo classes.
You need to pass a selector (or list of selectors) to the :has selector to apply it to the parent element.
The :has selector allows you to select the parent element based on the existence of elements inside it.
You can use the :has selector to set styles on the parent element based on the elements inside it, like adding a margin to the parent element when a certain child element exists.
The :has selector is useful for applying styles to parent elements based on their contents, which is not possible with normal CSS selectors.
Practical Examples
The :has() selector is a powerful tool that allows you to target a parent element based on its child elements.
You can use the :has() selector to apply styles to a section element that contains a paragraph element, as seen in the example where section is the target element to which the style color: red applies.
In real-world projects, you can use the :has() pseudo-class to create a modal effect, as demonstrated in the modal example where a checkbox triggers a style change when ticked.
The :has() selector can simplify your code and make it more efficient, as shown in the modal example where it replaces the need for JavaScript.
By using the :has() selector, you can write more concise and effective CSS code, making it easier to manage and maintain your projects.
Previous Sibling
The :has selector is a powerful tool that allows you to target elements based on the presence of other elements. It accepts a relative selector list, making it possible to select an element if it has another element that follows it, also known as a previous sibling.
You can use :has to style an input label based on the validity of its corresponding input. For example, you can target the label that has an invalid input as its next sibling.
With the :has selector, you don't need to place the label after the input or use CSS position: absolute; to style it. You can simply target the previous sibling like so: :has( + input:invalid ) ~ label.
This makes your code clearer and more succinct, as you can see in the example code.
Browser Compatibility and Support
The CSS :has selector has limited browser compatibility at the moment. Only the latest Safari browser has enabled it by default.
Currently, Chrome and other browsers do not support the CSS :has selector. However, you can enable it in Chrome by visiting chrome://flags/ and searching for "experimental Web Platform features".
To test for support, you can use a @supports feature query with the selector() function. This will allow you to target supporting browsers in a future-friendly way.
The upcoming version 101 of Chrome will also enable the CSS :has selector via the Experimental Web Platform features flag. This is a promising development, but it will take some time until the majority of browsers support it.
Syntax and Usage
The :has pseudo-class syntax is straightforward. It accepts a CSS selector list as arguments, and is forgiving, ignoring any invalid selectors passed as arguments.
The syntax is as follows: :has(selector list). For example, :has(p, li). You can chain selectors to target child elements of a target element or adjacent element.
Here's a summary of the syntax in a table:
The :has pseudo-class is useful for targeting elements based on complex conditions, and can be combined with other pseudo-classes like :not to create complex relational selectors.
Syntax
The :has pseudo-class syntax is quite straightforward. It accepts a CSS selector list as arguments.
Like other CSS pseudo-classes, the selector list is "forgiving", meaning CSS :has ignores any invalid selectors passed as arguments.
To use :has, you'll need to provide a selector for the element that will be targeted if the condition is met, as well as a condition defined with a CSS selector that needs to be met for styles to be applied.
Here's a breakdown of the syntax:
- Selector for an element that will be targeted if condition passed as an argument to :has pseudo-class has been met.
- A condition defined with a CSS selector that needs to be met for styles to be applied to the selector.
Note that selectors can be chained to target child elements of a target element or adjacent element.
Advanced Topics
The :has selector is incredibly versatile, and it's amazing how much you can do with it. You can pass CSS combinator selectors as arguments in :has(), allowing you to match elements with more specificity.
Using combinator selectors like + can be particularly useful. For example, you can match p elements that immediately follow an h2 with h2 + p. This is unlike a simple CSS selector, which would only match the first p element that follows an h2.
You can also use more complex combinator selectors, like the one that matches list items that have a paragraph followed by another paragraph: li:has(p + p). This kind of selector can be really powerful for matching complex patterns on the page.
The :has selector can also match elements that directly contain other elements. For instance, the selector p:has(span) matches p elements that directly contain a span child.
Specificity and Performance
Calculating specificity with the :has selector is a bit complex compared to a normal pseudo class. If you're unfamiliar with how CSS specificity is calculated, check out the ultimate CSS specificity guide.
A single :has selector with one selector inside it has a straightforward specificity calculation, combining the specificity of the :has selector with the specificity of the outside selector.
The specificity of a selector with a single :has selector and one selector inside it is 2 classes and 1 element. This is the same as the specificity of the below code.
Multiple selectors in one :has selector only use the specificity of the most specific selector. This means that if the .subtitle selector is more specific than the p selector, the specificity inside the :has selector will be just 1 class.
The overall specificity of a selector with multiple :has selectors is the combination of each :has selector's specificity and the overall specificity of the selector. For example, this selector has an overall specificity of 2 classes and 1 element.
Beyond Parenting
The :has selector is a game-changer for complex CSS selection. It allows you to select elements based on the presence of other elements within them.
For example, you can use it to select all p tags that are inside a .heading element as long as that .heading element contains a .subtitle element. This is a huge step up from traditional parent selectors.
The :has pseudo-class takes a relative selector list and will then represent an element if at least one other element matches the selectors in the list. This means you can create some incredibly complex selectors that can achieve things that were previously thought impossible.
You can use :has to select all button elements which include an svg element, like an icon. This is a great way to style icons in your UI.
By combining :has with the :not pseudo-class, you can select an element if it does not contain certain other elements. For example, you can select an article if it does not have a headline inside it.
Current Approach and Workarounds
Currently, web developers rely on JavaScript to apply styles and CSS classes to dynamically loaded elements, which can lead to cluttered and inefficient code.
Toggling a loading CSS class on the parent element with JavaScript is a common approach to indicate that data is being fetched.
This approach can be frustrating, especially when dealing with complex UI components and conditional styling.
The lack of a robust CSS solution forces developers to resort to JavaScript, which can compromise the quality and performance of their code.
Using skeleton loaders and dynamically loaded elements can be a challenge, but there are workarounds that can help alleviate the issue.
Developers often use JavaScript to apply styles and CSS classes to elements that are conditionally rendered, which can be a maintenance nightmare.
The current approach can be improved with a more efficient and robust CSS solution, like the relational selector prototype, which is expected to extend the range and use-cases for existing selectors.
Content-Based Variations
Developers need to keep track of various CSS classes when creating multiple variations based on content, location on the page, child state, etc.
This can lead to a lot of variation CSS classes, making component styles difficult to manage and maintain, and increasing the possibility of bugs caused by human error.
With a relational CSS selector, developers would be able to write content checks directly in CSS and styles would be applied automatically.
This would reduce the amount of variation CSS classes and decrease the possibility of bugs caused by human error.
Developers would no longer need to document all variations and use-cases using tools like Storybook, as selectors would be self-documented with the condition checks.
This approach would simplify the process of managing complex component styles and reduce the risk of errors.
Sources
- selector (caniuse.com)
- other CSS pseudo-classes (mozilla.org)
- pseudo-class (mozilla.org)
- Brian Kardell has recently announced (bkardell.com)
- not supported in any browsers (caniuse.com)
- GitHub repo (github.com)
- CSS Level 4 Selectors (w3.org)
- Try the CodePen demo (codepen.io)
- a CodePen demo again (codepen.io)
- Try it on CodePen. (codepen.io)
- this brilliant demo by Michelle Barker (css-irl.info)
- As Michelle Barker notes (css-irl.info)
- only browser with support (caniuse.com)
- Using :has() as a CSS Parent Selector and much more (webkit.org)
- The CSS :has Selector (and 4+ Examples) (css-tricks.com)
- The CSS :has() selector is way more than a “Parent Selector” (bram.us)
- CSS :has A Parent Selector And It Is Better Than I Ever ... (webdevsimplified.com)
Featured Images: pexels.com