Skip to content

[selectors] :nth-child(n of A) should ignore default namespaces for A, same as :is() #9804

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
tabatkins opened this issue Jan 16, 2024 · 3 comments
Labels
selectors-4 Current Work

Comments

@tabatkins
Copy link
Member

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 with A: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 from AB to A: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; changing svg|a.foo to svg.a:nth-child(n of .foo) makes it stop matching anything at all, if the default namespace is not SVG.

@Loirooriol Loirooriol added the selectors-4 Current Work label Jan 17, 2024
@Loirooriol Loirooriol changed the title :nth-child(n of A) should ignore default namespaces for A, same as :is() [selectors] :nth-child(n of A) should ignore default namespaces for A, same as :is() Jan 17, 2024
@tabatkins
Copy link
Member Author

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.)

@Loirooriol
Copy link
Contributor

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?

@tabatkins
Copy link
Member Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
selectors-4 Current Work
Projects
None yet
Development

No branches or pull requests

2 participants