Re: [selectors-api] Scoped Selectors

2009-09-30 Thread Sean Hogan

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 :scopeem, :scopestrong.  Of 
course, it 

Re: [selectors-api] Scoped Selectors

2009-09-27 Thread Lachlan Hunt

Boris Zbarsky wrote:

On 9/26/09 4:36 PM, Lachlan Hunt wrote:

A scoped selector string is a string that begins with an exclamation
point followed by a the remainder of the selector.


This assumes that '!' will never be allowed at the beginning of a CSS
selector, right?


It does, but the workaround would be to insert an extra space at the 
beginning.


I'd be willing to pick an alternative character to avoid any possible 
clash.  Perhaps a comma instead, like ,div div, which can never be 
used at the beginning of a conforming group of selectors.


I'm also considering adjusting the idea it so that it will work when the 
string simply begins with any combinator ('+', '~', or ''), but we 
still need something to use in place of the descendant combinator as the 
space won't have the desired effect.



Have you run this by the CSS working group?


Not yet, but will do so after I find out from this group if the 
technique is viable.



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 :scopeem, :scopestrong. Of course, it
will also work with the other combinators.


That processing still needs to be defined, right?


Yes.  It would be useful to get feedback from implementers about how I 
should define this.


--
Lachlan Hunt - Opera Software
http://lachy.id.au/
http://www.opera.com/



Re: [selectors-api] Scoped Selectors

2009-09-26 Thread Lachlan Hunt

John Resig wrote:

3. Obtain a collection of elements based on their relation to more than one
specified reference elements.

e.g.
Query to the document to obtain elements matching :scope+span, where
:scope is intended to match any of the elements in a specific collection.
  This would be simpler than iterating all of the nodes in the collection,
running the query on each of them and then merging the results.


I don't see the purpose of making a distinction between the root node used
for the query and the node being used for the scope - they should be one and
the same.

// Doesn't make sense:
document.querySelectorAll(div div, document.body)

// Does make sense
document.body.querySelectorAll(div div)


It does make sense.  It just means something slightly different from 
what you appear to be thinking.  In the above, the document.body element 
wouldn't have any effect because it's not a scoped selector and there is 
no :scope pseudo-class used.



Also, I don't think it's been made clear in the discussion thus far but
while cases like handling   div are nice - we're mostly concerned about
cases like div div (that escape outside the original root of the query).


The problems with handling div, +div and div div are exactly the 
same issue.  The only difference is that the first 2 begin with a child 
combinator and sibling combinator, respectively, and the latter has an 
implied descendant combinator.  They can all be addressed with the same 
solution.



Given this DOM:

body
   div id=one
 div id=two/div
   /div
/body

// All of these should return nothing
document.getElementById(one).querySelelctor(div div)
document.getElementById(one).querySelelctor(body div)
document.getElementById(one).querySelelctor(div #two)


The real benefit of the API as I first designed it, is that it elegantly 
provides ways to address nearly every use case raised and more, using 
the simple, yet extremely powerful concept of contextual reference 
elements, which is in fact not so different from the pattern used in 
JQuery: $(selector, context);.



There doesn't need to be scoping for matchesSelector. matchesSelector
implies that it it's starting from the specified node and doing a match.
Additionally the use case of .matchesSelector(  div) doesn't really exist.


The use case doesn't really exist when the context node is considered to 
be the contextual reference element.  But allowing reference nodes to be 
specified, it allows the query to check where it is in relation to 
another element.  So, for example you could more easily check if the 
parent of element elm is one of the nodes in the collection elms.


var elms = [div1, div2, div3];
e.g. elm.matchesSelector(:scope*, elms);

Returns true if the elm.parent == div1, elm.parent == div2 or elm.parent 
== div3.



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);

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.


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 :scopeem, :scopestrong.  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 

Re: [selectors-api] Scoped Selectors

2009-09-26 Thread Boris Zbarsky

On 9/26/09 4:36 PM, Lachlan Hunt wrote:

A scoped selector string is a string that begins with an exclamation
point followed by a the remainder of the selector.


This assumes that '!' will never be allowed at the beginning of a CSS 
selector, right?  Have you run this by the CSS working group?



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 :scopeem, :scopestrong. Of course, it
will also work with the other combinators.


That processing still needs to be defined, right?

-Boris



Re: [selectors-api] Scoped Selectors

2009-09-25 Thread Sean Hogan

Hi Lachy,

Here's a proposal.

querySelector*(selector, context) // allows selectors with :scope 
pseudo-class
queryScopedSelector*(selector, context) // allows selectors with implied 
:scope
matchesSelector(selector, context) // allows selectors with :scope 
pseudo-class


To check if the :scope pseudo-class is available, use:

try { document.body.matchesSelector(:scope, document.body); }
catch (error) { /* not supported */ }

 OR

try { document.querySelector(:scope, document.body); }
catch (error) { /* not supported */ }


Now, querySelector*() can't accept selectors with implied :scope because 
while  em is unambiguously :scope  em, p em would become 
ambiguous. (is it p em or :scope p em)

So we need a new method, such as queryScopedSelector*().
element.querySelector*() limits selection to descendants of elements, 
and element.queryScopedSelector*() should be consistent.
If element is the scope then element.queryScopedSelector*(~p) will 
return no elements.
If we want to support sibling queries then we need to provide a scope 
explicitly, so:


   element.parentNode.queryScopedSelector*(~p, element);

Notes:
1. I don't think browsers should provide queryScopedSelector*()
2. I think :context is a better name than :scope
3. If the context argument of these methods could be an element or a 
NodeList it might satisfy some of the other feature requests.




Lachlan Hunt wrote:

Hi,
   I'm trying to find a suitable solution for the scoped selector
issues, but figuring out what the most suitable API is proving 
challenging.



*Use Cases*

1. JS libraries like JQuery and others, accept special selector 
strings beginning with combinators. e.g. em,+strong. These 
libraries behave as if there was a selector that matched the context 
node.


e.g. In JQuery:

$(+p, elm);

This would select the p element that is a sibling of elm.

2. It would be useful to be able to check if an a given element 
matches a selector in relation to a specified reference element 
(:scope).  For example, check if an event target is a sibling of a 
specific element, and if the parent element has a specifc class name set.


e.g. Matches the selector: .foo:scope~input[type=text]

This may be particularly useful for event delgation.

3. Obtain a collection of elements based on their relation to more 
than one specified reference elements.


e.g.
Query to the document to obtain elements matching :scope+span, where 
:scope is intended to match any of the elements in a specific 
collection.  This would be simpler than iterating all of the nodes in 
the collection, running the query on each of them and then merging the 
results.



*Problems*

1. Need a way to allow the browser to parse implicitly scoped 
selectors beginning with combinators and imply the presence of :scope 
before each in the group.


2. Need to allow :scope to be used within the selector strings, and 
specify one or more scope elements that will be matched by :scope.  
This needs to be useable with all of the querySelector(), 
querySelectorAll() and matchesSelector() methods, or others with 
equivalent functionality.


3. Ideally, there would be an easy, reliable way for scripts to test 
if the implementation supports scoped selectors (at least, implicitly 
scoped selectors. Those using :scope could only be discovered by 
capturing the SYNTAX_ERR exception)  For legacy browsers that don't, 
they can fall back to their own selector engines.



*Possible Solutions*

1. Define a Selector object that can be used to parse and store a
selector, and which can handle pre-parsing the selector and
specifying the scope elements upon creation.  This selector object
can then be passed anywhere that accepts a selector string. (This is
basically part of the createSelector() and Selector interface
proposal from earlier).

2. Add parameters to the querySelector(), querySelectorAll() and
matchesSelector() methods for:
a. Indicating whether the selectors parameter should be processed
   with an implied scope.
b. Specifying one or more reference elements that would match :scope.

3. Create new scoped versions of the existing methods that accept one
or more reference elements that would match the implied scope.
Add an optional parameter to the existing querySelector*() methods
that would Allow one or more reference elements to be specified to
match the explicit use of :scope in the selector.


Option 2 doesn't provide an easy way to detect browser support.  
Option 3 creates an additional queryScopedSelector*() and 
matchesScopedSelector() methods, but this could get quite verbose if 
we also add equivalent NS methods to handle the namespace issue, to 
both the scoped and non-scoped versions.  This would create an 
unreasonable number of different methods that would make understanding 
the API quite complex.  Option 1 is syntactically messy, and requires 
the creation of a new object just to handle a scoped selector, even if 
that selector is 

Re: [selectors-api] Scoped Selectors

2009-09-25 Thread Sean Hogan

Sean Hogan wrote:

Hi Lachy,

Here's a proposal.

querySelector*(selector, context) // allows selectors with :scope 
pseudo-class
queryScopedSelector*(selector, context) // allows selectors with 
implied :scope
matchesSelector(selector, context) // allows selectors with :scope 
pseudo-class


To check if the :scope pseudo-class is available, use:

try { document.body.matchesSelector(:scope, document.body); }
catch (error) { /* not supported */ }

 OR

try { document.querySelector(:scope, document.body); }
catch (error) { /* not supported */ }

Sorry. Replace document.body with document.documentElement. That should 
be more efficient for the querySelector test.




Re: [selectors-api] Scoped Selectors

2009-09-25 Thread Lachlan Hunt

Sean Hogan wrote:

Here's a proposal.

querySelector*(selector, context) // allows selectors with :scope
pseudo-class
queryScopedSelector*(selector, context) // allows selectors with implied
:scope
matchesSelector(selector, context) // allows selectors with :scope
pseudo-class


Yes, this is effectively the same as option #2 that I described, except 
you haven't provided a way to support implied scope there.



To check if the :scope pseudo-class is available, use:

try { document.body.matchesSelector(:scope, document.body); }
catch (error) { /* not supported */ }


I'm not sure that relying on exceptions for feature testing is an ideal 
way to provide that functionality.



Now, querySelector*() can't accept selectors with implied :scope because
while  em is unambiguously :scope  em, p em would become
ambiguous. (is it p em or :scope p em)
So we need a new method, such as queryScopedSelector*().


Well, we at least need some way to tell the implementation to 
pre-process the selector to imply the presence of :scope. 
queryScopedSelector*() would do this, as would providing an explicit 
pre-processing step, the result of which can be passed to the 
querySelector*() methods.



element.querySelector*() limits selection to descendants of elements,
and element.queryScopedSelector*() should be consistent.
If element is the scope then element.queryScopedSelector*(~p) will
return no elements.
If we want to support sibling queries then we need to provide a scope
explicitly, so:

element.parentNode.queryScopedSelector*(~p, element);

Notes:
1. I don't think browsers should provide queryScopedSelector*()


This seems contradictory.  You seemed to be proposing that we use 
queryScopedSelector, and now you're saying we shouldn't.  Personally, I 
agree that we shouldn't.  It's my least favourite solution of them all.



2. I think :context is a better name than :scope


Yeah, the name of :scope is a complicated issue. :context isn't ideal 
either.  It would be slightly confusing because selectors API defines 
the term context node as being the node upon which the method is being 
invoked.  Maybe something like :ref or :reference might work.



3. If the context argument of these methods could be an element or a
NodeList it might satisfy some of the other feature requests.


Yes, the reference elements parameter will accept either a single 
element, Array or NodeList.


I have checked in a new draft containing my first attempt at supporting 
scoped selectors, with support for both :scope (or whatever it gets 
called) and implied scope selectors.  I've opted for a combination of 
options 1 and 2 that I previously described, using the createSelector() 
and SelectorExpression object for being able to represent implied scoped 
selectors easily, and optional refNodes parameters on querySelector*() 
and matchesSelector() methods for supplying contextual reference 
elements (that match :scope).


Basically, for the simple case, it works as illustrated in these examples:

elm.querySelector(:scopep);
document.querySelectorAll(:scopep, elm);
document.querySelectorAll(:scopep, [elm1, elm2]);

To provide the functionality of JS libraries supporting implied scope 
selectors, you first create a SelectorExpression object like this:


document.createSelector(em,strong, true);

That object can then be passed to either the querySelector*() or 
matchesSelector() methods.


The effect of this is basically that JavaScript libraries can mostly use 
document.createSelector(str, true) as a direct replacement their own 
custom selector parsing libraries (except for the cases where they're 
using custom pseudo-classes not supported by the browser)


One possible modification I'm considering is introducing a separate 
factory method for creating implied scope selectors: 
createScopedSelector(selector); rather than using a boolean parameter.


--
Lachlan Hunt - Opera Software
http://lachy.id.au/
http://www.opera.com/



Re: [selectors-api] Scoped Selectors

2009-09-25 Thread Boris Zbarsky

On 9/25/09 9:59 AM, Lachlan Hunt wrote:

Yes, the reference elements parameter will accept either a single
element, Array or NodeList.


What about HTMLCollection?  Or have we finally made that interface 
inherit from NodeList?


Another question I just had reading this, and this is probably a webidl 
question, not a selectors API question, is what makes something an 
element, array, or nodelist.  First off, what if some spec introduces 
objects that implement both the Element and NodeList interfaces? 
Second, what if I do:


  function foo() {
  }
  foo.prototype = Array.prototype
  var myObj = new foo();

Is that an array?  How should the callee be able to determine this?

What about:

  var elm = document.createElement(div);
  function foo() {
  }
  foo.prototype = elm;
  var myObj = new foo();

For what it's worth, it looks like in this case 
myObj.appendChild(document.createTextNode(test)) appends to |elm| in 
Gecko and Google Chrome, throws WRONG_THIS_ERR in Opera, and throws 
Type error in Safari.  Doing document.body.appendChild(myObj) throws 
WRONG_ARGUMENTS_ERR in Opera, throws NOT_FOUND_ERR in Safari and Chrome, 
and throws HIERARCHY_REQUEST_ERR in Gecko  How is this case 
different from the array case, if at all?


-Boris



Re: [selectors-api] Scoped Selectors

2009-09-25 Thread John Resig
 3. Obtain a collection of elements based on their relation to more than one
 specified reference elements.

 e.g.
 Query to the document to obtain elements matching :scope+span, where
 :scope is intended to match any of the elements in a specific collection.
  This would be simpler than iterating all of the nodes in the collection,
 running the query on each of them and then merging the results.


I don't see the purpose of making a distinction between the root node used
for the query and the node being used for the scope - they should be one and
the same.

// Doesn't make sense:
document.querySelectorAll(div div, document.body)

// Does make sense
document.body.querySelectorAll(div div)

Also, I don't think it's been made clear in the discussion thus far but
while cases like handling  div are nice - we're mostly concerned about
cases like div div (that escape outside the original root of the query).

Given this DOM:

body
  div id=one
div id=two/div
  /div
/body

// All of these should return nothing
document.getElementById(one).querySelelctor(div div)
document.getElementById(one).querySelelctor(body div)
document.getElementById(one).querySelelctor(div #two)



 *Problems*

 1. Need a way to allow the browser to parse implicitly scoped selectors
 beginning with combinators and imply the presence of :scope before each in
 the group.

 2. Need to allow :scope to be used within the selector strings, and specify
 one or more scope elements that will be matched by :scope.  This needs to be
 useable with all of the querySelector(), querySelectorAll() and
 matchesSelector() methods, or others with equivalent functionality.

 3. Ideally, there would be an easy, reliable way for scripts to test if the
 implementation supports scoped selectors (at least, implicitly scoped
 selectors. Those using :scope could only be discovered by capturing the
 SYNTAX_ERR exception)  For legacy browsers that don't, they can fall back to
 their own selector engines.


 *Possible Solutions*

 1. Define a Selector object that can be used to parse and store a
selector, and which can handle pre-parsing the selector and
specifying the scope elements upon creation.  This selector object
can then be passed anywhere that accepts a selector string. (This is
basically part of the createSelector() and Selector interface
proposal from earlier).

 2. Add parameters to the querySelector(), querySelectorAll() and
matchesSelector() methods for:
a. Indicating whether the selectors parameter should be processed
   with an implied scope.
b. Specifying one or more reference elements that would match :scope.

 3. Create new scoped versions of the existing methods that accept one
or more reference elements that would match the implied scope.
Add an optional parameter to the existing querySelector*() methods
that would Allow one or more reference elements to be specified to
match the explicit use of :scope in the selector.


 Option 2 doesn't provide an easy way to detect browser support.  Option 3
 creates an additional queryScopedSelector*() and matchesScopedSelector()
 methods, but this could get quite verbose if we also add equivalent NS
 methods to handle the namespace issue, to both the scoped and non-scoped
 versions.  This would create an unreasonable number of different methods
 that would make understanding the API quite complex.  Option 1 is
 syntactically messy, and requires the creation of a new object just to
 handle a scoped selector, even if that selector is only used once.

 I'm not sure which alternative would be best, and I'm kind of hoping
 there's a 4th alternative I haven't thought of yet that can address the use
 cases easliy enough.


There doesn't need to be scoping for matchesSelector. matchesSelector
implies that it it's starting from the specified node and doing a match.
Additionally the use case of .matchesSelector( div) doesn't really exist.

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

Same length as getElementsByClassName - long but not untenable.

--John


Re: [selectors-api] Scoped Selectors

2009-09-25 Thread Sean Hogan

Lachlan Hunt wrote:

Sean Hogan wrote:

Here's a proposal.

querySelector*(selector, context) // allows selectors with :scope
pseudo-class
queryScopedSelector*(selector, context) // allows selectors with implied
:scope
matchesSelector(selector, context) // allows selectors with :scope
pseudo-class


Yes, this is effectively the same as option #2 that I described, 
except you haven't provided a way to support implied scope there.


That's because implied scope is incompatible with :scope ~ p or ~ p 
should we want to support those. I think it will be confusing to have 
implied and explicit forms for :scope.



element.querySelector*() limits selection to descendants of elements,
and element.queryScopedSelector*() should be consistent.
If element is the scope then element.queryScopedSelector*(~p) will
return no elements.
If we want to support sibling queries then we need to provide a scope
explicitly, so:

element.parentNode.queryScopedSelector*(~p, element);

Notes:
1. I don't think browsers should provide queryScopedSelector*()


This seems contradictory.  You seemed to be proposing that we use 
queryScopedSelector, and now you're saying we shouldn't.  Personally, 
I agree that we shouldn't.  It's my least favourite solution of them all.




I don't think implied :scope selector text should be supported at all. 
It's a whim of the JS libraries. I'm just concerned that if we do have 
it then at least it doesn't screw up the core functionality.



2. I think :context is a better name than :scope


Yeah, the name of :scope is a complicated issue. :context isn't ideal 
either.  It would be slightly confusing because selectors API defines 
the term context node as being the node upon which the method is 
being invoked.  Maybe something like :ref or :reference might work.




Yeah.


3. If the context argument of these methods could be an element or a
NodeList it might satisfy some of the other feature requests.


Yes, the reference elements parameter will accept either a single 
element, Array or NodeList.


I have checked in a new draft containing my first attempt at 
supporting scoped selectors, with support for both :scope (or whatever 
it gets called) and implied scope selectors.  I've opted for a 
combination of options 1 and 2 that I previously described, using the 
createSelector() and SelectorExpression object for being able to 
represent implied scoped selectors easily, and optional refNodes 
parameters on querySelector*() and matchesSelector() methods for 
supplying contextual reference elements (that match :scope).


Basically, for the simple case, it works as illustrated in these 
examples:


elm.querySelector(:scopep);
document.querySelectorAll(:scopep, elm);
document.querySelectorAll(:scopep, [elm1, elm2]);

To provide the functionality of JS libraries supporting implied scope 
selectors, you first create a SelectorExpression object like this:


document.createSelector(em,strong, true);

That object can then be passed to either the querySelector*() or 
matchesSelector() methods.


The effect of this is basically that JavaScript libraries can mostly 
use document.createSelector(str, true) as a direct replacement their 
own custom selector parsing libraries (except for the cases where 
they're using custom pseudo-classes not supported by the browser)


One possible modification I'm considering is introducing a separate 
factory method for creating implied scope selectors: 
createScopedSelector(selector); rather than using a boolean parameter.





Looks okay, except I don't think there should be implied contextual 
reference elements.
What are the chances that we will have to extend createSelector in the 
future? e.g. namespaces





[selectors-api] Scoped Selectors

2009-09-24 Thread Lachlan Hunt

Hi,
   I'm trying to find a suitable solution for the scoped selector
issues, but figuring out what the most suitable API is proving challenging.


*Use Cases*

1. JS libraries like JQuery and others, accept special selector strings 
beginning with combinators. e.g. em,+strong. These libraries behave 
as if there was a selector that matched the context node.


e.g. In JQuery:

$(+p, elm);

This would select the p element that is a sibling of elm.

2. It would be useful to be able to check if an a given element matches 
a selector in relation to a specified reference element (:scope).  For 
example, check if an event target is a sibling of a specific element, 
and if the parent element has a specifc class name set.


e.g. Matches the selector: .foo:scope~input[type=text]

This may be particularly useful for event delgation.

3. Obtain a collection of elements based on their relation to more than 
one specified reference elements.


e.g.
Query to the document to obtain elements matching :scope+span, where 
:scope is intended to match any of the elements in a specific 
collection.  This would be simpler than iterating all of the nodes in 
the collection, running the query on each of them and then merging the 
results.



*Problems*

1. Need a way to allow the browser to parse implicitly scoped selectors 
beginning with combinators and imply the presence of :scope before each 
in the group.


2. Need to allow :scope to be used within the selector strings, and 
specify one or more scope elements that will be matched by :scope.  This 
needs to be useable with all of the querySelector(), querySelectorAll() 
and matchesSelector() methods, or others with equivalent functionality.


3. Ideally, there would be an easy, reliable way for scripts to test if 
the implementation supports scoped selectors (at least, implicitly 
scoped selectors. Those using :scope could only be discovered by 
capturing the SYNTAX_ERR exception)  For legacy browsers that don't, 
they can fall back to their own selector engines.



*Possible Solutions*

1. Define a Selector object that can be used to parse and store a
selector, and which can handle pre-parsing the selector and
specifying the scope elements upon creation.  This selector object
can then be passed anywhere that accepts a selector string. (This is
basically part of the createSelector() and Selector interface
proposal from earlier).

2. Add parameters to the querySelector(), querySelectorAll() and
matchesSelector() methods for:
a. Indicating whether the selectors parameter should be processed
   with an implied scope.
b. Specifying one or more reference elements that would match :scope.

3. Create new scoped versions of the existing methods that accept one
or more reference elements that would match the implied scope.
Add an optional parameter to the existing querySelector*() methods
that would Allow one or more reference elements to be specified to
match the explicit use of :scope in the selector.


Option 2 doesn't provide an easy way to detect browser support.  Option 
3 creates an additional queryScopedSelector*() and 
matchesScopedSelector() methods, but this could get quite verbose if we 
also add equivalent NS methods to handle the namespace issue, to both 
the scoped and non-scoped versions.  This would create an unreasonable 
number of different methods that would make understanding the API quite 
complex.  Option 1 is syntactically messy, and requires the creation of 
a new object just to handle a scoped selector, even if that selector is 
only used once.


I'm not sure which alternative would be best, and I'm kind of hoping 
there's a 4th alternative I haven't thought of yet that can address the 
use cases easliy enough.


--
Lachlan Hunt - Opera Software
http://lachy.id.au/
http://www.opera.com/