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.

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.

Works in all modern browsers. May need a fallback for older browsers.

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

Your first month is free

Managed WordPress hosting for faster sites.

Learn more
⚑

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 600+ readers who've survived clearfix hacks.

ESC