Selecting parent elements without JavaScript
Selecting a parent based on its children used to require JavaScript. :has() handles it in pure CSS, no event listeners needed.
2 border-color: red;
3 background: #fff0f0;
4}
5/* Pure CSS. Zero JavaScript. */
2document.querySelectorAll('input')
3 .forEach(input => {
4 input.addEventListener('invalid', () => {
5 input.closest('.form-group')
6 .classList.add('has-error');
7 });
8 });
Browser Support for :has()
Since 2023 this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.
No JavaScript needed
Eliminates an entire class of DOM-manipulation code. Fewer event listeners, fewer bugs.
Instant response
Browser applies styles in the rendering pipeline, no waiting for JS execution or reflow.
Composes naturally
Chain with other selectors: .nav:has(.dropdown:hover), body:has(dialog[open]).
How it works
:has() is a relational pseudo-class. When you write .card:has(img), it selects any .card that contains an img as a descendant. It's the parent selector that CSS lacked for 25 years.
You can use any selector inside :has(): pseudo-classes like :invalid, :checked, :hover, or even combinators like :has(> .direct-child). It unlocks conditional styling that previously required JavaScript.