CSS :has() Selector: Select Parent by Child

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.

Old wayJS required
// Watch for changes, find parentdocument.querySelectorAll('input')  .forEach(input => {    input.addEventListener('invalid', () => {      input.closest('.form-group')        .classList.add('has-error');    });  });
3 lines
.form-group:has(input:invalid)   {  border-color: red;  background: #fff0f0;}
Widely availableSince 202394% global usage

This feature is well established and works across many devices and browser versions. It has been available across browsers since 2023.

Safe to use without fallbacks.

105+
121+
15.4+
105+
type in the field to see :has() react
:has(input:invalid) selects the parent, no JS needed

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]).

Lines Saved
8 → 3
JS → pure CSS
Old Approach
JavaScript
Event listeners + DOM
Modern Approach
Pure CSS
Zero runtime cost

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.

ESC