-
Notifications
You must be signed in to change notification settings - Fork 719
[css-selectors] has-child selector #4903
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
Comments
Hopefully most of the respondents actually did mean parent ( @argyleink @una Do you have any gut feeling regarding this? The survey says "parent selector", which we're interpreting as "immediate parent selector", whereas I'm worried some respondents actually want "ancestor selector" (which would be way more expensive). |
Regarding performance cost, it's more similar to indirect adjacent or :nth-* selectors than direct sibling selectors. In Blink, invalidation sounds straightforward, quite similar to how we invalidate for the previously mentioned selectors. :nth-selectors are typically implemented with a cache (at least Blink and Gecko/Servo) to avoid repeated O(n) traversals for multiple siblings. :has-child() would only be done once for a set of siblings, since they can only have a single parent, but if you have multiple :has-child() selectors matching the same element, we would possibly want to avoid multiple passes over the children somehow for :has-child(). The current spec text for :has() does not limit this to the rightmost compound. It's not obvious to me that: "div a:has-child(img) span {}" does not have more performance implications than only allowing :has-child() in the rightmost compound. |
Hmm, I don't think invalidation sounds so straight-forward? Now dom mutations can invalidate styles of ancestors, which as far as I know all engines (including Blink) assume cannot happen. Sure, it's only one-level ancestor, but it is not if you nest them, right? So |
And at that point you may as well just implement full |
Yes, div:has-child(div:has-child(img)) is essentially a different syntax for a variant of :has, so my assumption would be that :has-child() only takes a compound or compound list, not allowing nested has-child() or any other simple selectors which can contain combinators sigh, and as previously mentioned only in the rightmost compound. In Blink, the invalidation for nth-* selectors is similar to what we would do here. Mark an element if it either matched or failed to match a :has-child() selector, and when any of its children mutates and the flag is set, mark that parent for style recalc. |
Just want to agree with @andruud here that I am a little unconvinced that these are not confused in the poll and overlapping. I asked this back in Feb, connected by them via twitter on just how it was even worded? but didn't get any reply. In most of my conversations, by parent selector, many people seem to have different interpretations
I'm just one person tho, and understand that's all anecdotal. It's also not to say that something very limited has no use... It feels worth a nice post explaining a proposal and its limits and getting feedback from devs though first? |
@bkardell I agree that we don't have enough data to say whether parent means parent or ancestor, or similar potential abiguities. A post explaining and gathering feedback would be excellent. Were you volunteering to help with that? Would be great if so, just checking. |
In previous discussions of this (largely predating us using GH, unfortunately), it was presumed that nesting So you could have I assume we'd definitely impose this restriction, else this is just a less convenient way of writing |
Thinking about it again, limiting this to rightmost compound would not be a requirement. Tab's "div:has-child(img) + label:has-child(:checked)" example, or "div:has-child(img) span" should also have similar performance implications. |
From my experience folks exclusively mean ancestor querying. Eg, they want a child to own how it responds to a class or state findable further up the DOM. @bkardell had it right when using the |
@chrishtr |
The So another approach to this would be to restrict And if that resolves the performance issues regarding In a later level, Sebastian |
PrinceXML has already implemented |
Yes, printing implementations can implement :has() reasonably; the problem with :has() is that it means the style of an element can be affected by the qualities of any element on the entire page, and so progressive rendering and efficient updates become vastly more difficult and/or expensive to do. If your implementation isn't bound by web reality in having to get correct pixels on screen during loading and after each mutation absolutely ASAP, then the downsides aren't killer, and :has() is a nice useful feature.
No simplification of :has() that still allows arbitrary combinators is likely to have a noticeable effect on the perf. It's not the cost of evaluating the selector that's killer, it's the number of elements that have to be re-evaluated on every mutation. Currently it's all the descendants of the mutated element's parent; they're the only elements that can observe the mutation with today's selectors. :has() makes the entire document potentially able to observe the mutation. :has-child() widens the set of possibly-reacting elements to all the descendants of the mutated element's grandparent, which can be a considerably larger set, but is still much less than the entire document.
I'm not sure what you mean here - you seem to be talking about normal descendant combinators. |
I'm sure this was just a misread, |
@tabatkins, I know it's very expensive and unlikely be be implemented in browsers. My comment was a reply to @SebastianZ - I'd suggest drastically changing the meaning of the |
Right, ignoring what syntax Adam used, his description sounds like he's just talking about descendant combinators. Elements react to their ancestors today. |
@tabatkins I'm sorry, I'm not sure what you are clarifying here so I'm not sure if I need to clarify in turn. Apologies if I am repeating or clarifying something obvious, but ...
|
Right, I get that's what But Adam's comment said:
That is just standard selectors:
Ooooof, no, this would be real bad: contextual re-interpretation of the entire selector based on a combinator that might not show up until the very end! That's a "garden-path" issue, where your initial read seems to lead you down one direction, only for something at the end to make you realize you were going the wrong way the whole time, requiring you to back up and re-interpret the entire thing. Not to mention it makes selectors like |
The syntax I suggested is meant to be a subset of the currently specified one, and by that should not be a breaking change. Implementations already covering the currently defined syntax allowing complex selectors are then just ahead of time. Another option would be to already define both syntaxes in Level 4 and add a hint that implementations should support the fully-featured syntax if performance is irrelevant (e.g. in print engines) and fall back to the simpler syntax if performance is critical (like in browser engines).
The suggested syntax with a single Sebastian |
I just wanted to throw my 2 cents in here -- I've used the In that case, how would |
|
Sebastian |
Regarding the direct child vs. ancestor conversation, I think we agree that most people generally want "ancestor", as deeply nested components are common (and becoming increasingly common with framework-based UI systems). From a developer experience point of view, That being said, I'd rather have something than nothing. |
@una Note that if you use multiple ancestor:has-child(.container):has-child(.celebrate) {} should behave like ancestor:has-child(.container.celebrate, .container ~ .celebrate, .celebrate ~ .container) {} In order to go "two levels up" from ancestor:has-child(.container:has-child(.celebrate)) {} which some comments above assumed wouldn't be allowed. |
@una but would developers be able to use it effectively? That is hard to gauge. Any additional data on whether this would be helpful in practice would help to prioritize this feature. |
I'd guess that any native CSS solution would be arguably more effective than most JS-based workarounds that developers still have to use instead of it... |
I am an infrequent user of css. However, today, I came across the need to select all ancestors of an element. It would have been nice to have had a css selector to identify all ancestors of a given element to use in conjunction with javascript's querySelectorAll() It is easy to find all the descendants of an element using *. It feels like it should be as easy to find all parents. As is, I had to use something like this https://gomakethings.com/how-to-get-all-parent-elements-with-vanilla-javascript/ or like this |
If I'm not overlooking things, this issue may be closed now that With it, we can use |
|
@chrishtr I think we can close this out in favor of |
\o/ |
Proposal: add a selector that matches an element if a direct child matches the selector in the function.
"all a elements that contain an img" ->
a:has-child(img)
Motivation: #2 missing-features request in the state-of-CSS 2019 survey.
Since it only matches direct children, it may be no more expensive than sibling selectors.
A related selector that was considered and removed as too expensive and slow (AIUI) is :has. See also resolution regarding it here.
@tabatkins @lilles
The text was updated successfully, but these errors were encountered: