Open radogado opened 4 years ago
Isn't this already possible to express with complex selectors inside :not()
(as Selectors Level 4 allows): .wrapper .child:not(.child .child)
?
Isn't this already possible to express with complex selectors inside
:not()
(as Selectors Level 4 allows):.wrapper .child:not(:child .child)
?
What is :child? :not kind of works if we hardcode the number of containers between the top one and the children, but a proper solution would be better. https://codepen.io/radogado/pen/dyYyBWN
I donʼt fully understand the use case. It sounds like at least one of the following selectors should satisfy the constraints. Perhaps, a further :not(…)
would be needed.
.wrapper > * > .child
.wrapper * > .child
.wrapper > * .child
.wrapper * .child
.wrapper > * > .child:first-child
.wrapper * > .child:first-child
.wrapper > * .child:first-child
.wrapper * .child:first-child
.wrapper > :first-child > .child
.wrapper :first-child > .child
.wrapper > :first-child .child
.wrapper :first-child .child
I donʼt fully understand the use case. It sounds like at least one of the following selectors should satisfy the constraints. Perhaps, a further
:not(…)
would be needed.
.wrapper > * > .child
.wrapper * > .child
.wrapper > * .child
.wrapper * .child
.wrapper > * > .child:first-child
.wrapper * > .child:first-child
.wrapper > * .child:first-child
.wrapper * .child:first-child
.wrapper > :first-child > .child
.wrapper :first-child > .child
.wrapper > :first-child .child
.wrapper :first-child .child
It's not about specifying a strict structure like *.wrapper > > .child or the position between siblings (:first-child). Nearest descendant means if a .wrapper has a .child** descendant on level 3 and level 4, we select the nearest level (3). I need it to freely nest any component inside any component without conflicts. Between the following 2 cases,
.wrapper div div .child
.wrapper div div div .child
I need the first one to be selected. Thanks.
What is :child?
Oops, just a typo, sorry :( I meant simply .child
. Anyway, I also misread you initial example and for some reason assumed that the second .child
is nested inside another .child
, so my suggestion was incorrect :(
It sounds like you're describing the nearest descendent in document order, is that right? So if you had two elements that matched .wrapper > div > .child
, only the first one would match your test?
.wrapper > * > :nth-child(1 of .child),
.wrapper > * > :not(.child) > :nth-child(1 of .child),
.wrapper > * > :not(.child) > :not(.child) > :nth-child(1 of .child),
... and so on, depending on how deep your structure goes.
Not terribly elegant, but better than trying to evaluate
.wrapper > * > :nth-child(1 of .child:not(:has(.child)))
which I think might be equivalent, although I really don't fancy proving that. Also, you probably don't have :has()
!
@radogado I think what you want is
.wrapper > .child,
.wrapper:not(:has(> .child)) > * > .child,
.wrapper:not(:has(> .child, > * > .child)) > * > * > .child,
.wrapper:not(:has(> .child, > * > .child, > * > * > .child)) > * > * > * > .child,
/* ... */
i.e. select a 1st level .child
, or a 2nd level one if there is no 1st level one, or a 3rd level one if there is no 1st or 2nd level one, and so on.
You need :has()
for that, because in order to know if a .child
descendant matches, you need to look at all other descendants of .wrapper
with a smaller nesting level. But :has()
is expensive, so it has only been implemented in print engines.
Just because something can be implemented with :has()
, doesn't mean it's equivalent to :has()
; :has()
is a very big hammer and can pound a lot of nails. ^_^
This is just a request for "first descendant matching a selector", which yeah, has been a common request over the years. @radogado's original example is a somewhat novel showing of it, I think (usually it's "I have nested components, and I want to select the nearest one only". Looks like it was last discussed in 2015 on the mailing list.
The simpler form that's just about nesting isn't too bad. .foo /closest/ .bar .baz
would be matched as:
- Is this element a
.baz
? If no, bail.- If so, walk the ancestors until I find a
.bar
. If none, bail.- Continue walking the ancestors, looking for either a
.bar
or.foo
. If I find a.bar
, bail. If I find nothing, bail.- Yay, the element matched!
In other words it matches just like a normal descendant combinator, just checking the compound selectors on either side of the combinator, rather than just the one on the left. Slightly more expensive, but not a huge deal. It also might slightly widen the space of mutations to watch for to indicate you need to rerun styling on a subtree.
But the more general form that @radogado opened with where the non-matched element might be in a different subtree than the matched one would be substantially more expensive, and I suspect would kick us out of the realm of possibility.
Something that goes into the same direction and I had several use cases over the years is to match something x levels down in the tree structure and you know the exact number of levels. Could that be covered by this, too?
Using @tabatkins' syntax that could be done by .foo /x/ .bar
where x is the number of levels the right side of the selector is separated from the left part.
So for example, .wrapper /3/ .child
applied to @radogado's structure
<div class="wrapper">
<div>
<div class="child"> </div>
</div>
<div>
<div>
<div class="child"> </div>
</div>
</div>
</div>
would match the latter <div class="child">
because it's three levels down.
Sebastian
Isn't .wrapper /3/ .child
basically the same as .wrapper > * > * > .child
?
Thanks for the comments. A lot of people assumed I know the exact structure, which was never the idea and would be easily solvable. Here is a rephrasing of the HTML, where the ellipsis represent unknown nested levels of elements.
<div class="wrapper">
...
<div class="child"> </div>
...
<div>
...
<div class="child"> </div>
...
</div>
</div>
Isn't
.wrapper /3/ .child
basically the same as.wrapper > * > * > .child
?
Yes, though the deeper the child is placed the longer is the chain of > *
, which quickly becomes unreadable.
Sebastian
Hello, I would like to have a selector for the closest child only, which isn't a direct descendant of the container. It is useful for a carousel with a child (e.g. holding controls) and nested carousels, which shouldn't inherit the styles of the topmost one, while allowing for extra wrappers or supporting cases where we don't fully control the HTML structure.
.wrapper ^ .child to select only the first .child:
This has been requested by others before on SO. Thanks.