Re: [selectors] Proposal: filtered sibling combinator

On Fri, Oct 10, 2014 at 11:31 PM, Benjamin Poulain <bpoulain@apple.com> wrote:
> Hi,
>
> One problem I am interested in solving is the inability to format documents that do not have a strong hierarchical structure.
>
> For example, let's take:
>     <h1>Title 1</h1>
>     <h2>Subtitle</h2>
>     <p>Paragraph1</p>
>     <p>Paragraph2</p>
>     <h1>Title 2</h1>
>     <p>Paragraph3</p>
>     <p>Paragraph4</p>
> A common use case is styling the first paragraph after a title (for example, using ::first-letter). We can try:
>     h1+p::first-letter
> but that is too strong, it does not patch Paragraph1. We can try:
>     h1~p::first-letter
> but that is too weak, every paragraph matches.
>
>
> My current idea to solve those cases is to extend the ~ combinator to take a selector filtering the traversal. Something of the form:
>     a ~(b) c
>
> Traversal to find "c" would
> 1) Evaluate "c" on a sibling.
> 2) If [1] fails, evaluate b on the sibling.
> 3) If [2] succeed, go to the next sibling.
> 4) If [2] fails, fail to match.
>
> The use case above can be written has:
>     h1 ~(:not(h1, p)) p
> Which is traverse from <h1> to a <p> but never skip over a <h1> or <p>.
>
>
> The same principle could be used for descendant selector for generality.
> For example, an image not in a link:
>     img:not(:any-link *)
> can be rewritten as
>     * >>(:not(:any-link)) img
> which is cleaner in my opinion.

This one doesn't work - it'll match "<a><span><img></span></a>".  You
need to verify that there's no <a> ancestors at all, like ":root
>>(:not(:any-link)) img"

> Any interest?

Thanks for bringing up this case explicitly. ^_^

Okay, so it's a functional combinator somewhere between "next" and
"general" (for both siblings and descendants), qualified with a
selector to determine exactly what kinds of elements its allowed to
skip over.  It looks like ~(*) and >>(*) are both equivalent to the ~
and >> combinators by themselves, yes?

Overall, I think I like it.  I hadn't thought of actually making a
functional combinator, so that's pretty cool.  It shouldn't be too
much of a complication of the processing model, either - when running
selectors backwards, it just means you're checking the selector
against the previous siblings/ancestors.  It's basically just a
helpful syntax, as you can achieve an identical effect by just
repeating the selector multiple times, like `A + C, A + B + C, A + B +
B + C, A + B + B + B + C, ...`.

Just to throw it out, an alternate syntax might be to lean on the
/foo/ syntax for named combinators, which would look something like
`h1 /sibling :not(h1,p)/ p` or `:root /child :not(:any-link)/ img`.  A
benefit of this is that we no longer need to come up with *names* for
the general forms of these combinators: if you don't pass any
arguments, they just refer to the next child or sibling; if you want
the general form, just pass a * argument, like `/sibling */`.
(That'll close a comment if you don't have any whitespace between the
* and /, but that's fine; as long as you're not in a comment, */ has
no special meaning and is just two DELIM tokens.)

~TJ

Received on Saturday, 11 October 2014 14:59:26 UTC