W3C home > Mailing lists > Public > public-webapps@w3.org > July to September 2009

Re: [selectors-api] Scoped Selectors

From: Sean Hogan <shogun70@westnet.com.au>
Date: Thu, 01 Oct 2009 08:46:38 +1000
Message-ID: <4AC3DFCE.2060106@westnet.com.au>
To: Lachlan Hunt <lachlan.hunt@lachy.id.au>
CC: John Resig <jeresig@gmail.com>, public-webapps <public-webapps@w3.org>
Lachlan Hunt wrote:
> John Resig wrote:
>
>> With that in mind, option #3 looks the best to me. It's lame that the 
>> API
>> will be longer but we'll be able to use basic object detection to see 
>> if it
>> exists. Unfortunately the proper scoping wasn't done the first time the
>> Selectors API was implemented so we kind of have to play the hand 
>> we've been
>> dealt.
>>
>> Thus there would be two new methods:
>> queryScopedSelectorAll
>> queryScopedSelector
>
> I really didn't want to introduce new methods for this if it could be 
> avoided.  I realise one problem with the first draft of the API I 
> posted yesterday was that is was too cumbersome for scripts to create 
> and use scoped selectors, rather than normal selectors.  That draft 
> required scripts to do the following:
>
> var selector = document.createSelector("+p", true);
> document.querySelector(selector, elm);

This isn't cumbersome:

- JS libraries are still going to provide their own query functions 
which can wrap this trivially

- people who want to use the standard API will also want to use standard 
selectors

- the tiny group of people who don't want to use a JS library but do 
want to use selector strings with implied :scope will just create a 
wrapper function (or method).

Element.prototype.queryScopedSelector = function(selector, scope) {
    return this.querySelector(document.createSelector(selector, true), 
scope);
}

This is just as simple as the new proposal.

Element.prototype.queryScopedSelector = function(selector, scope) {
    return this.querySelector("!" + selector, scope);
}


>
> I have come up with a significantly simpler, alternative solution that 
> not only abolishes the createSelector() method and the 
> SelectorExpression interfaces, but also avoids introducing additional 
> methods like queryScopedSelector(), or extra parameters.
>
> The draft now defines the concept of a *selector string* and a *scoped 
> selector string*.  The selector string is just an ordinary selector, 
> as supported by current implementations.
>
> A scoped selector string is a string that begins with an exclamation 
> point followed by a the remainder of the selector.  The purpose of the 
> exclamation point is to clearly identify the string as a scoped 
> selector that requries an extra pre-processing step to turn it into a 
> valid group of selectors.
>
> There are also slightly different requirements for the processing 
> Element.querySelectorAll() when the selector argument is a scoped 
> selector string.  This allows for the sibling combinator cases to work.

That is quite inconsistent behavior.

- querySelector*() and matchesSelector() can now take standard and 
non-standard selector strings

- matchesSelector() can have explicit declaration of the ::reference 
element, while querySelector*() can have explicit and implied

- element.querySelector*() now has quite complex behavior.
With one selector string it selects from descendants of element, with 
another is selects from descendants of element.parentNode.
If you want "element" to be the :reference node then it doesn't need a 
second argument. But if you want "element" and another node to be 
:reference then you have to pass both in an array as the second argument.


It is also less flexible - it makes using non-standard selector strings 
slightly easier but using standard selector strings is now more difficult.

- if I want to provide an API that doesn't accept these non-standard 
selector strings then I can no longer just wrap querySelector*(). 

- if I want to provide an API that doesn't return siblings of the 
context-node then I can no longer just wrap querySelector*().

To illustrate that last point using the previous and current drafts:
To support "+p" in previous draft is trivial.

Element.prototype.queryScopedSelectorAll = function(selector, ref) {
    return 
this.parentNode.querySelectorAll(document.createSelector(selector, 
true), ref);
}

To NOT support ":reference + p" in current draft we have to filter 
siblings from the result.

Element.prototype._querySelectorAll = Element.prototype.querySelectorAll;
Element.prototype.querySelectorAll = function(selector, ref) {
    var parent = this.parentNode;
    var nodes = this.querySelectorAll(selector, ref);
    return Array.filter(nodes, function(node) {
       return (node.parentNode == parent) ? false : true;
    });
}

To NOT support "+ p" in current draft have to reject scoped selector 
syntax and filter siblings.

Element.prototype._querySelectorAll = Element.prototype.querySelectorAll;
Element.prototype.querySelectorAll = function(selector, ref) {
    var parent = this.parentNode;
    if (/^(>|+|~|!)/.test(selector)) throw "";
    var nodes = this._querySelectorAll(selector, ref);
    return Array.filter(nodes, function(node) {
       return (node.parentNode == parent) ? false : true;
    });
}



>
> e.g. The selector ">em, >strong" supported by JS libraries can simply 
> be prefixed with a "!", like "!>em, >strong" and the implementation 
> will be able to process it to become ":scope>em, :scope>strong".  Of 
> course, it will also work with the other combinators.
>
> This allows JS libraries to trivially prepend "!" to the selector 
> before passing it to the API, rather than requiring any complicated 
> pre-processing.  In current browser implementations, the "!" will 
> trigger a syntax error and allow JS libraries to fallback to their 
> custom processing.
>
> The following examples illustrate how this API can be used to address 
> the use cases.  Assume the variable elm refers to a single element and 
> and elms refers to  a collection of elements, such as an array or 
> nodelist.
>
>
> Use Case: Select elements based on their relationship to one specific 
> element.  i.e. Given an Element elm, be able to select child, 
> grandchild or sibling elements.
>
> In JQuery:
>
>   $(">em, >strong", elm);
>   $("div div", elm);
>   $("+p", elm)
>
> or the equivalents using $(elm).find("...");
>
> To do either of these in Selectors API, use:
>
>   // Equivalent to ":scope>em, :scope>strong". Use either:
>   document.querySelectorAll("!>em, >strong", elm);
>   elm.querySelectorAll("!>em, >strong");
>
>   // Equivalent to ":scope div div". Use either:
>   document.querySelectorAll("!div div", elm);
>   elm.querySelectorAll("!div div");
>
>   // Equivalent to ":scope+p". Use either:
>   document.querySelectorAll("!+p", elm);
>   elm.querySelectorAll("!+p");
>
>
> Use Case: Select elements based on their relationship to elements in a 
> collection.  i.e. Given a collection of elements elms, be able to 
> select child or grandchild elements, or sibling elements, of each 
> element in that collection.
>
> In JQuery:
>   $(">em, >strong", elms);
>   $("div div", elms);
>   $("+p", elms)
>
> This effectively works the same as the previous use case.  Just create 
> the selectors in the same way, and pass the elms collection to 
> querySelectorAll(), as in:
>
>   document.querySelectorAll("!+p", elms);
>
>
> Use Case: Given a collection of elements, find a subset that meets 
> specific conditions.  e.g. Given an array of elements elms, select 
> only those that have a specific class name.
>
> For instance, a document contains several input elements, like:
>
> <input type="text" name="addr-line1" class="required">
> <input type="text" name="addr-line2">
> <input type="text" name="addr-suburb" class="required">
> <input type="text" name="addr-country">
>
> In JQuery:
> var elms = $("input");
> // do something with elms.
> // Now select only the required controls for additional processing:
> var filterdList = elms.filter(".required");
>
> document.querySelectorAll(".required:scope", elms)
>
> (There's currently no shorthand syntax that can be used by JS 
> libraries to have this generated automatically.  It might be worth 
> defining one analogous to scoped selector strings.)
>
> The one limitation of the above is that if the elms collection 
> contained nodes from both within the document and disconnected 
> elements, only matching elements within the document would be 
> selected.  (The results would be similar, if this was done on a 
> DocumentFragment or disconnected Element node).  So it's not quite 
> identical to running the query on each individual element and merging 
> the results.  But it's not clear whether or not such a use case is 
> significant.  It seems more natural to want to restrict the results to 
> those within the same tree.
>
> See the current editors draft for more details.
> http://dev.w3.org/2006/webapi/selectors-api2/
>
Received on Wednesday, 30 September 2009 22:47:01 GMT

This archive was generated by hypermail 2.3.1 : Tuesday, 26 March 2013 18:49:33 GMT