- From: Sean Hogan <shogun70@westnet.com.au>
- Date: Thu, 01 Oct 2009 08:46:38 +1000
- 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 UTC