w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.43k stars 656 forks source link

[css-selectors] Proposal: "Middle Out" sibling selectors #8483

Open caucik opened 1 year ago

caucik commented 1 year ago

Currently we can select elements using :nth-child, :nth-last-child, :nth-of-type and :nth-last-of-type based on An+B [of S] from either beginning or end of the list of siblings.

These selectors also work great with dynamic number of siblings. We can specify for example every 3rd element starting from 5th, first/last 3 elements etc. regardless of the number of siblings in the collection.

Oftentimes we need to select sibling nodes around given element (pagination, breadcrumb navigation, effects around :hover-ed element like this or this etc.).

We can utilize E + F and :has() selectors but there are multiple problems with it. Apart from the apparent asymmetry of the "forward" vs. "backward" selectors in the rule it only works for fixed number of sibling elements. And so for fixed solution we need to specify one rule per one level of siblings around given element:

It is a solution but not a pretty one nor a dynamic one. It is also not immediately obvious what it means. Therefore it's closer to a hack than to clean solution.

I propose two new pseudo selectors with obvious functionality:

:nth-prev-sibling(An+B [of S]?)

and

:nth-next-sibling(An+B [of S]?)

With these two selectors we can conveniently select elements the same way as using current pseudo selectors mentioned in first paragraph of this proposal but instead of anchoring at the beginning or end of nodeList we can anchor at the selected element anywhere inside of nodeList.

A few examples:

a a a a a a a a.active a a a a a a a a

to select 1 element before a.active: a.active:nth-prev-sibling(1)

to select all but first 3 elements after a.active a.active:not(:nth-next-sibling(-n+3))

Obviously it won't be free. But we already have :not() and :has() so why bother.

tabatkins commented 1 year ago

Can you give some examples of this being used in practice? I've personally never had to do more than the trivial "element before/after a chosen element" thing, which seems reasonably fine to write out by hand.

Loirooriol commented 1 year ago

Note that a.active:nth-prev-sibling(1) matches a subset of a.active, so presumably it would be equivalent to a.active:has(+ *). 1 element before a.active could be :nth-prev-sibling(1 of a.active) (but then the meaning of the selector argument is different than in :nth-child, the full functionality would maybe need two selectors?)