- From: François REMY <francois.remy.dev@outlook.com>
- Date: Sat, 11 Oct 2014 17:59:32 +0200
- To: "'Tab Atkins Jr.'" <jackalmage@gmail.com>, "'Benjamin Poulain'" <bpoulain@apple.com>
- CC: "'www-style list'" <www-style@w3.org>
> 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