Open tabatkins opened 10 months ago
In the telcon discussion about #5684, dbaron summarized the justification of the :is()/etc behavior really well: we don't want to have the default namespace applied twice to the same element, via different selectors, such that it's easy to not override the default on one of them and get an inconsistent and confusing behavior.
So like in svg|a.foo
, you only apply the rule once (nothing happens, since it already has an explicit namespace. In svg|a:is(.foo)
you want to apply it only once as well, so you don't get an incompatible selector (effectively svg|a:is(default|*.foo)
). But in a case like svg|a:is(div .foo)
, it's fine for the default namespace to apply to the div
selector, since that's a different element, same as if it were written div svg|a.foo
.
So the general policy to draw here is, the default namespace rule should not apply to any pseudoclass's selector argument, for the component of that selector that applies to the same element as the pseudoclass itself.
Adopting this as a general policy would obviate this issue; it would automatically fall under the new rule. We could also remove the exception from :is()/etc, since they'd be auto-covered.
Notably, this does not cover :has()
's argument; since that selector is relative, none of its components apply to the element the :has()
is on.
(And to be clear, this would only apply to parts that are defined as applying to the subject element. Writing svg|a:has(:is(.foo *))
wouldn't do anything special to the .foo
part, even if it ends up matching the svg|a:has(...)
element, because that match-up is coincidental rather than by definition.)
And to be clear, this would only apply to parts that are defined as applying to the subject element
I think this can get a bit tedious, like in svg|a > :is(.foo > *)
the .foo
must be the same as the svg|a
because elements have a single parent, but is this considered to be defined as applying to the same element?
No, that's exactly the opposite of what I said. ^_^ In :is()
, the subject of the selector argument is the thing that's defined to be the same as the pseudo's own element. Any other part of the selector would only match up by other bits of the selector by coincidence.
The
:is()
,:not()
, and:where()
pseudos all have text that states that if the final compound selector in their arguments doesn't have a type selector, we ignore the general rule that requires them to match the default namespace (when one exists).(That is, given
@namespace "http://example.com";
, a selector like.foo
is implicitly only selecting elements from that default namespace; you have to explicitly write*|*.foo
to get back to the normal behavior of allowing any namespace to match. This doesn't apply to:is()
/etc arguments.)This special behavior exists to ensure that the combo pseudoclasses intuitively work correctly when used as syntax sugar; that is, given any selector
AB
, replacing it withA:is(B, C)
is guaranteed to match all the same elements, and possibly more. See #5684 for more details.This same argument should apply to the
n of A
arguments to:nth-child()
/etc: going fromAB
toA:nth-child(n of B)
should match the exact same elements. Currently (lacking the special rule that:is()
/etc have), this isn't necessarily true; changingsvg|a.foo
tosvg.a:nth-child(n of .foo)
makes it stop matching anything at all, if the default namespace is not SVG.