{"id":376342,"date":"2023-01-11T06:18:13","date_gmt":"2023-01-11T14:18:13","guid":{"rendered":"https:\/\/css-tricks.com\/?p=376342"},"modified":"2023-01-11T06:18:16","modified_gmt":"2023-01-11T14:18:16","slug":"has-is-an-unforgiving-selector","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/has-is-an-unforgiving-selector\/","title":{"rendered":":has is an unforgiving selector"},"content":{"rendered":"\n

A little thing happened on the way to publishing the CSS :has()<\/code> selector<\/a> to the ol’ Almanac. I had originally described :has()<\/code> as a “forgiving” selector, the idea being that anything in its argument is evaluated, even if one or more of the items is invalid.<\/p>\n\n\n\n

\/* Example: Do not use! *\/\narticle:has(h2, ul, ::-scoobydoo) { }<\/code><\/pre>\n\n\n\n

See ::scoobydoo<\/code> in there? That’s totally invalid. A forgiving selector list ignores that bogus selector and proceeds to evaluate the rest of the items as if it were written like this:<\/p>\n\n\n\n

article:has(h2, ul) { }<\/code><\/pre>\n\n\n\n\n\n\n\n

:has()<\/code> was indeed a forgiving selector in a previous draft dated May 7, 2022<\/a>. But that changed after an issue was reported<\/a> that the forgiving nature conflicts with jQuery when :has()<\/code> contains a complex selector (e.g. header h2 + p<\/code>). The W3C landed on a resolution to make :has()<\/code> an “unforgiving” selector<\/a> just a few weeks ago.<\/p>\n\n\n\n

So, our previous example? The entire selector list is invalid because the bogus selector is invalid. But the other two forgiving selectors, :is()<\/a><\/code> and :where()<\/a><\/code>, are left unchanged.<\/p>\n\n\n\n

There’s a bit of a workaround for this. Remember, :is()<\/code> and :where()<\/code>are forgiving, even if :has()<\/code> is not. That means we can nest either of the those selectors in :has()<\/code> to get more forgiving behavior:<\/p>\n\n\n\n

article:has(:where(h2, ul, ::-scoobydoo)) { }<\/code><\/pre>\n\n\n\n

Which one you use might matter because the specificity of :is()<\/code> is determined by the most specific item in its list. So, if you need to something less specific you’d do better reaching for :where()<\/code> since it does not add to the specificity score.<\/p>\n\n\n\n

\/* Specificity: (0,0,1) *\/\narticle:has(:where(h2, ul, ::-scoobydoo)) { }\n\n\/* Specificity: (0,0,2) *\/\narticle:has(:is(h2, ul, ::-scoobydoo)) { }<\/code><\/pre>\n\n\n\n