W3C home > Mailing lists > Public > www-style@w3.org > September 2009

Re: Supporting Scoped Selectors in Selectors API 2

From: Lachlan Hunt <lachlan.hunt@lachy.id.au>
Date: Tue, 29 Sep 2009 01:49:38 +0200
Message-ID: <4AC14B92.7080400@lachy.id.au>
To: fantasai <fantasai.lists@inkedblade.net>
Cc: www-style <www-style@w3.org>
fantasai wrote:
> Lachlan Hunt wrote:
>> fantasai wrote:
>>> I agree with Tab here, the use of ! is a strange way of handling special
>>> cases like this. Its exceptional use is confusing, and its scoping is
>>> inconsistent with the way selectors work. I'd rather see something like
>>>
>>> elm.scopeSelector("div div, div p")
>>
>>> I think it's much clearer how that works and it avoids screwing around
>>> with the Selectors syntax.
>>
>> No, it absolutely does require screwing around with the selector
>> syntax regardless because it needs to allow each selector in the group
>> to have its first simple selector omitted and begin with a combinator.
>>
>> e.g. ">em, +strong, ~b, i"
>>
>> Each of those needs to have an implied :reference pseudo-class
>> inserted before it, and the last also needs an implied descendant
>> combinator inserted.
>
> Or, you could not allow any shortcuts here and require :reference (or
> :scope, as Tab recommends, and I second) to be inserted explicitly in
> such cases. I would imagine they're less common than the descendant case.

Are you now suggesting that we should not introduce new methods and just 
require that authors explicitly use :reference in each selector in the 
current methods, or are you suggesting we do introduce new methods, 
require authors to use :reference, and find some way to deal with the 
problematic cases where they don't?

In either case, it simply won't work.

Using existing methods only:

   elm.querySelectorAll(":reference+p");

This will never match anything.  It can't because the method can only 
ever match descendant elements.

Introducing new methods, but still requiring explicit :reference won't 
work either, or at least not in any sane way.

Consider this document:

<body>
   <p>paragraph 1
   <div id="foo">
     <p>paragraph 2
   </div>
   <p>paragraph 3
</body>

   var elm = document.getElementById("foo");

   var p2 = elm.queryScopedSelector(":reference p"); // paragraph 2
   var p3 = elm.queryScopedSelector(":reference+p"); // paragraph 3

It's clear what each of these two methods return. But what about when 
authors inevitably forget to include it?

   elm.queryScopedSelectorAll("p");

What would be the result in that case?  The implementation needs to test 
all elements in the document to see if it matches the selector.  Without 
having any constraints on it's relationship to the reference element, it 
would match both paragraphs 2 and 3.  Depending on the optimisations 
done by the browser, such as whether or not to bother testing previous 
siblings and ancestor elements, paragraph 1 may or may not be matched.

So you again end up with a situation where you basically need to imply 
the presence of :reference and a descendant selector at the beginning to 
actually get a sane result.

>> The point is that in order to solve the problem, we need some kind of
>> indicator to say that this is a scoped selector. This indicator is
>> used for two purposes:
>>
>> 1. Altering selector parsing to allow selectors to begin with
>> combinators and to insert implied :reference pseduo-class at the
>> beginning.
>>
>> 2. Addressing the sibling element problem by modifying the selection
>> processing so that they can be selected as well. e.g. In the case of
>> ":reference+p". Note that the current methods are restricted to
>> descendant elements only.
>>
>> The solutions considered so far include:
>>
>> 1. New queryScopedSelector() and queryScopedSelectorAll() methods.
>> ...
>> The first option is messy because it requires the introduction of so
>> many new methods, and it gets even more messy if we need to introduce
>> namespaced versions in the future like querySelectorNS(),
>> querySelectorAllNS(), queryScopedSelectorNS() and
>> queryScopedSelectorAllNS().
>
> Sorry, I don't understand this. Why do you need separate methods for
> namespaced versions? (What does it mean, a namespaced version of
> querySelector?)

Selectors leave the issue of namespace resolution to an external 
mechanism.  In CSS, this is @namespace.  In XBL2, xmlns declarations are 
used.  In the API, there needs to be some other mechanism available to 
to resolve the prefixes used in the selectors.  Using seprate NS methods 
that accept an extra namespsace resolver as an argument would be 
somewhat in line with how other *NS() methods in the DOM work.

>> 4. A special syntactic flag in the selectors argument, as used in the
>> current proposal.
>>
>> That left me with the fourth and final option that I decided to try
>> and see if it will work. I tried to make it as benign as possible, so
>> that it is a flag that is stripped from the beginning of the string
>> before selector parsing begins. i.e. You don't need to use it at the
>> beginning of each selector in the group. (e.g. "!div, p" becomes
>> ":reference div, :reference p").
>
> This is worse than a boolean flag. How is this not worse than a boolean
> flag?

As I said, the boolean flag parameter provided absolutely no way for 
scripts to detect whether or not implementations support scoped 
selectors.  Using the syntactic flag instead at least caused a syntax 
error to be thrown by existing implementations, allowing scripts to 
catch it and resort to fallback behaviour.

>> The final option is to simply forgo the special parsing entirely and
>> require authors and javascript libraries to insert explicit :reference
>> pseudo-classes at the beginning of each selector, but we'd still need
>> to find some way of addressing the sibling element problem, and that
>> would require authors to use a more complicated approach like:
>>
>> elm.parentNode.querySelectorAll(":reference+p", elm);
>>
>> But that makes things more complicated because scripts would first
>> need to check if the element has a parent node, which it may not in
>> the case of disconnected elements, and then fallback to alternative
>> processing.
>
> Sorry, but I don't understand how your special syntax make a difference
> here.

As defined in the current draft, the processing rules for which elements 
to match are altered based on whether or not it's a scoped selector.

e.g.
elm.querySelectorAll("div"); will only ever return descendant elements. 
Match testing is never performed on sibling elements, and they are never 
returned.

elm.querySelctorAll("+div");

Match testing is performed against all descendants and siblings, and 
allows siblings to be returned in the results.

This is not feasible to do with

   elm.queyrSelectorAll(":reference+div");

Because, as I explained previously, the flag to enable this behaviour 
needs to be distinct from the pseudo-class itself to avoid even more 
complexity.

-- 
Lachlan Hunt - Opera Software
http://lachy.id.au/
http://www.opera.com/
Received on Monday, 28 September 2009 23:50:20 GMT

This archive was generated by hypermail 2.3.1 : Tuesday, 26 March 2013 17:20:21 GMT