:has vs :matches - Selectors Level 4
css, css-selectors
Solution
In a nutshell:
`E:has(rs1, rs2)` matches E when a different element F matches any of the selector arguments in relation to E. If you know jQuery's `:has()` selector, this is exactly the same thing.
`E:matches(s1, s2)` matches E when E itself matches any of the selector arguments. Think of `:matches()` as the direct opposite of `:not()`, which matches E if E itself does not match any of the arguments.1 You can also think of `:matches()` as a pseudo-class version of jQuery's `.filter()` method.
This notation is equivalent to concatenating every selector argument with E (provided you can actually concatenate them) such that you have a selector list `(E)(s1), (E)(s2)`. For example, `div:matches(.foo, .bar)` is equivalent to `div.foo, div.bar`.
This fundamental difference is demonstrated most straightforwardly with the selectors `div:matches(p)` and `div:has(p)`:
`div:has(p)` matches any `div` element that has a `p` descendant. This is very similar to `div p`, except the former targets the `div` and the latter targets the `p`.
Since a `div` can never be a `p`, `div:matches(p)` will never match anything. (Likewise, `div:not(p)` will match all `div` elements.)
Here's a more complex example with slightly less absurd selectors:
div:has(.foo, .bar)
div:matches(.foo, .bar)
With the following markup:
<div class="foo"></div> <!-- [1] -->
<div class="bar"></div> <!-- [1] -->
<div class="foo bar"> <!-- [2] -->
<p></p>
</div>
<div> <!-- [3] -->
<p class="foo"></p>
</div>
<div> <!-- [3] -->
<div> <!-- [3] -->
<p class="bar"></p>
</div>
</div>
<div> <!-- [4] -->
<div class="foo"> <!-- [5] -->
<p class="bar"></p>
</div>
</div>
Which elements are matched by which selectors?
Matched by `div:matches(.foo, .bar)` The first `div` element has the "foo" class, and the second `div` element has the "bar" class, so each of these satisfies its respective selector argument in the `:matches()` pseudo-class.
Matched by `div:matches(.foo, .bar)` The third `div` element has both classes, so it matches both selector arguments.
A note on specificity: both of these arguments have equal specificity, making the total specificity `(0, 1, 1)`, but when an element matches multiple selector arguments with different specificity values, the spec says that the specificity is that of the most specific argument that is matched.
Matched by `div:has(.foo, .bar)` Each of these `div` elements has a descendant element (in this case, a `p`) with a class that matches its respective selector argument in the `:has()` pseudo-class.
Matched by `div:has(.foo, .bar)` This `div` element has a `div.foo` descendant and a `p.bar` descendant, therefore it satisfies both relative selector arguments.
A note on specificity: because `:has()` is not yet in the fast profile and is therefore tentatively excluded from CSS, the concept of specificity does not apply at all. There are plans to include a limited version of this in the fast profile for use in CSS, but there is nothing concrete as yet. Any new developments will be added at an appropriate time.
Matched by `div:matches(.foo, .bar)` and `div:has(.foo, .bar)` This `div` element matches both pseudo-classes:
- it is `.foo` (as it has the "foo" class), and
- it has a descendant with the "bar" class.
This element would also match `div:matches(.foo, .bar):has(.foo, .bar)`, which would be a valid level 4 selector since a compound selector can have any combination of pseudo-classes.
Another difference between `:matches()` and `:has()` is that `:has()` accepts what's known as a relative selector. A relative selector has a scope; selector scoping is an entire topic in its own right, but for the purposes of `:has()`, the scoping element is always the element you attach the `:has()` pseudo-class to. But, more importantly, a relative selector can either have an implicit descendant combinator, or begin explicitly with a combinator such as `>`, `+` or `~` — this combinator is what links the rest of the relative selector to its scoping element.
For example, while `:has()` defaults to an ancestor-descendant relationship, you can pass a relative selector that begins with `+` and it then becomes an adjacent-sibling relationship: `ul:has(+ p)` matches any `ul` element that is directly followed by a `p` (and not necessarily one that contains a `p` descendant).
As for `:matches()`, while the overview table says that it accepts a list of compound selectors, AFAIK it hasn't yet been set in stone whether it will take a list of compound selectors or complex selectors, and in which profile (fast or complete). But a compound selector is simply the new name for what Selectors 3 currently calls a sequence of simple selectors and a complex selector is an entire series of compound selectors and combinators. A relative selector is more like a complex selector in that respect. See this answer for a non-exhaustive list of the various terms used in selectors.
1 Yes, that's "arguments" in plural — in Selectors 4, `:not()` can now accept a list of selectors as opposed to a single simple selector. A much-needed enhancement, but it's also to make it consistent with the other new functional pseudo-classes.
Problem
Just wondering what the difference is between CSS (selectors 4) pseudo selectors `:has` and `:matches` The spec http://dev.w3.org/csswg/selectors-4/#overview says: E:matches(s1, s2) an E element that matches compound selector s1 and/or compound selector s2 §4.2 The Matches-any Pseudo-class: :matches() E:has(rs1, rs2) an E element, if either of the relative selectors rs1 or rs2, when evaluated with E as the :scope elements, match an element §4.4 The Relational Pseudo-class: :has()