- 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