Has Isn't A Parent Selector. It Deletes JavaScript.
Search your code for classList.toggle.
You likely find many instances of the same pattern. A user clicks a checkbox or focuses a field. You use JavaScript to toggle a class on a parent element. This allows CSS to style the parent.
You can delete most of this code.
People call :has() the parent selector. This label is wrong. :has() tests a condition. It looks at the contents of an element to decide if a style applies.
It can check for:
- A descendant element
- A specific sibling
- A count of items
- The absence of an element
Here are some examples:
• Style a card if it contains an image: .card:has(img) { ... }
• Style a row when a checkbox is checked: .option-row:has(input:checked) { background: var(--selected-bg); }
• Change a grid layout based on item count: .grid:has(> :nth-child(4)) { grid-template-columns: repeat(4, 1fr); }
The last example changes a layout using pure CSS. It reads the live state of the DOM.
Before, you needed JavaScript to act as a messenger. A child could not trigger styles on an ancestor. You had to write a listener to move a class up the tree.
Now, the messenger is gone.
If you use :has(), your styles are always correct. They work on the first render. They work when a user goes back in their browser. There is no delay between the state change and the style change.
Use it for:
- Form validation
- Empty states in lists
- Toggleable panels
Note that :has() has a cost. The browser must evaluate the inner selector against descendants. For most UI tasks like validation or toggles, you will not notice a difference.
Avoid using it for high-frequency changes like scroll positions or thousands of hover states.
Stop toggling classes just to drive styles from a child. Ask if :has() can express that condition instead. Your CSS will shrink and your JavaScript will disappear.
:has() works in Chrome, Firefox, Safari, and Edge.
Source: https://dev.to/parsajiravand/has-isnt-a-parent-selector-it-deletes-javascript-4hej
