Selectors Intermediate

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.

βœ“ Modern
3 lines
1.form-group:has(input:invalid) {
2  border-color: red;
3  background: #fff0f0;
4}

5/* Pure CSS. Zero JavaScript. */
βœ• Old JS required
1// Watch for changes, find parent
2document.querySelectorAll('input')
3  .forEach(input => {
4    input.addEventListener('invalid', () => {
5      input.closest('.form-group')
6        .classList.add('has-error');
7    });
8  });
Newly available Since 2023 94% global usage

Since 2023 this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

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.

New CSS drops.

Join 400+ readers who've survived clearfix hacks.

ESC