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

I think those selectors are interesting, too, and relatively easy to support for browser vendors (which is a nice).

However, I'm quite possibly biased by my NLP interest, but it doesn't seem very obvious what the effective intention of a selector is when written using any of those two proposed syntax, even if you are familiar with them. What about something a little more verbose?
 
p ~ p    ===    p +(possibly-while *) p

 At each step of the loop, we can either decide to stop taking the previous sibling, or to continue. This means that we have a backtracking algorithm here.

h1:first-of-type +(until h1, p) p[title]
h1:first-of-type +(while :not(h1, p)) p[title]

 Selects the first paragraph that’s sibling with the first h1, provided no other H1 comes before it, and if it has a "title" attribute.
 This time, there's no condition at all and there's no backtracking involved.

h1:first-of-type +(possibly-until h1) p
h1:first-of-type +(possibly-while :not(h1)) p

 Selects all paragraphs that are sibling with the first h1, provided no other H1 comes before them.

Received on Saturday, 11 October 2014 15:59:57 UTC