Skip to content
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

[cssom-1] Expose nested selectorText on CSSStyleRule#10246

Open
bramus opened this issue Apr 24, 2024 · 13 comments
Open

[cssom-1] Expose nested selectorText on CSSStyleRule #10246

bramus opened this issue Apr 24, 2024 · 13 comments
Labels

Comments

@bramus
Copy link
Contributor

bramus commented Apr 24, 2024

On a CSSStyleRule you can get the selectorText that returns the textual representation of the selector.

With CSS nesting, this value has become somewhat less useful as it doesn’t represent the full selector.

While an author can use script to compute the flattened selector by manually walking the .parentRule to the top and then doing the checks + replacements for &, it would be nice if this was built into the platform. That way, an author can directly use it in something like document.querySelectorAll.

I’m thinking of a property named flattenedSelectorText or fullSelectorText.

@bramus bramus added the cssom-1 label Apr 24, 2024
@romainmenke
Copy link
Member

This would be very useful!

For all things PostCSS, Stylelint, ... we also need to determine the full selector and I created a helper for this : https://github.com/csstools/postcss-plugins/tree/main/packages/selector-resolve-nested#readme

The wording we use is a bit different : https://github.com/csstools/postcss-plugins/blob/main/packages/selector-resolve-nested/docs/selector-resolve-nested.md#functions

We use flatten for naive replacements. This does a simple replacement of & with the parent selector. The result is a mangled selector that won't match the same elements but this is useful for static analysis. (e.g. how many type selectors did the author write in source?)

We use resolve to get a full selector that would also match the same elements. (e.g. what is the specificity of this selector?)

If there was a specification for this action we should have similar behavior between tooling and browsers.

@bramus
Copy link
Contributor Author

bramus commented Apr 24, 2024

Great distinction between flattened and resolved. Given this, what I suggested would better be named resolvedSelectorText.

@Loirooriol
Copy link
Contributor

This seems vulnerable to a "billion laughs attack".

@romainmenke
Copy link
Member

romainmenke commented Apr 24, 2024

It is, but given that it is a script API it could have limits and throw when those limits are exceeded.

Edit : It is not.
This would serialize selectors with :is()

@Loirooriol
Copy link
Contributor

it could have limits and throw when those limits are exceeded

This seems very fragile. I would prefer a different design that doesn't suffer from this, e.g.

document.querySelectorAllWithNesting("a, b", "& c, & d", "& e, & f")

instead of

document.querySelectorAll("a c e, a c f, a d e, a d f, b c e, b c f, b d e, b d f")
document.querySelectorAll(":is(:is(a, b) c, :is(a, b) d) e, :is(:is(a, b) c, :is(a, b) d) f")

This would serialize selectors with :is()

This doesn't avoid the problem.

@romainmenke
Copy link
Member

romainmenke commented Apr 24, 2024

You are right, it makes it slightly smaller.

But still quickly explodes in cases like this one :

a,
b {
	&+&,
	&>& {
		&+&,
		&>& {
			color: green;
		}
	}
}

@tabatkins
Copy link
Member

The current OM does represent the full selector; the & selector is not evaluated by substitution, but rather refers directly to the elements selected by the parent rule.

Understanding the use-cases better would be useful. If it's about making it easier to select the same elements in qSA() as a particular style block, we can cater to that more directly. @Loirooriol's suggestion (multiple selector arguments, representing nested selectors) might work, for example. But also, you seem to be suggesting that authors would be willing to crawl the OM and obtain a selector directly off of the CSSStyleRule - how serious were you about that in particular as a use-case? Because we could also, say, allow qSA() to take a CSSStyleRule directly, and return the same set of elements the rule matches, if that code pattern seems realistic.

Romain mentions some preprocessor uses, notably determining the specificity. Is there more?

@romainmenke
Copy link
Member

preprocessor uses, notably determining the specificity. Is there more?

For preprocessors or author tooling in general? No, all variations on the same theme :)

e.g. determining if &::before {} would be valid, which it might not be if & represents something like ::after. This is essentially the same problem as calculating specificity.

You need to do something with & to be able to give feedback on what the author wrote.
This is true for any indirection (e.g. var())

@bramus
Copy link
Contributor Author

bramus commented Apr 25, 2024

The current OM does represent the full selector; the & selector is not evaluated by substitution, but rather refers directly to the elements selected by the parent rule.

Sorry, should have said “flattened” or “resolved” selector.

Understanding the use-cases better would be useful. If it's about making it easier to select the same elements in qSA() as a particular style block, we can cater to that more directly.

That’s my specific use-case indeed.

You seem to be suggesting that authors would be willing to crawl the OM and obtain a selector directly off of the CSSStyleRule - how serious were you about that in particular as a use-case?

It came up when writing a (rough) polyfill for ident() + attr() for this demo but admittedly not all authors write that type of stuff.

Another use case is doing an analysis of the loaded CSS (nesting depth, layers extraction, etc.) with userland code.

@LeaVerou
Copy link
Member

LeaVerou commented Apr 25, 2024

Agree that the OM needs to provide both. If selectorText produces the full/resolved selector, then localSelector could produce the local one.

@Loirooriol
Copy link
Contributor

@LeaVerou I'm confused, isn't the current selector the same as the local one?

@LeaVerou
Copy link
Member

@Loirooriol edited

@bramus
Copy link
Contributor Author

bramus commented Apr 26, 2024

@LeaVerou You still got it flipped, no?

selectorText is the existing property which returns the local selector. The ask in c0 is for a new property that returns the resolved selector.

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

No branches or pull requests

5 participants