Re: [SelectorsAPI] Thoughts on querySelectorAll

John Resig wrote:
> There's three major points that I wanted to discuss:
> 
> * DOMElement.querySelectorAll returning incorrect elements
> 
> This is the most critical issue. As it stands DOM Element-rooted
> queries are borderline useless to libraries - and users. Their
> default behavior is unexpected and confusing. Demonstrated with an
> example, using Dojo:
> 
>   <div><p id="foo"><span></span></p></div>
>   <script src="http://o.aolcdn.com/dojo/1.1.0/dojo/dojo.xd.js"></script>
>   <script>
>   var foo = document.getElementById("foo");
>   alert( dojo.query('div span', foo).length ); // should return nothing
>   alert( foo.querySelectorAll('div span').length ); // will return the SPAN
>   </script>
> 
> The demo can be run online here: 
> http://ejohn.org/files/bugs/qsa-root/dojo.html

If we were to do the implementation similarly to Dojo, then it would be 
impossible to select only child elements on the context node.  Consider 
this:

<div id=foo>
   <div> <!-- A -->
     <div> <!-- B -->
     </div>
   </div>
</div>

foo.querySelectorAll("div");

Assuming the selector won't match the context node (foo) itself, that 
would contain both descendant div elements (A and B).  But in the case 
where only child nodes of foo are wanted (A), this is impossible.

This following would not work because the previous assumption was that 
selectors would not match the element itself.

foo.querySelectorAll("#foo>div");

Dojo gives the same result for that scenario.

dojo.query('#foo>div', foo);

> * Combinator-rooted Queries
> 
> I read about some prior discussion concerning this (especially in
> relation to DOMElement.querySelectorAll-style queries). This is an
> important part of most libraries, as it stands. Maciej's proposed
> solution of using :root to allow for front-leading combinators is
> perfectly acceptable to me (where :root is made equivalent to the
> element, not the document element).
> 
> // jQuery $("#foo").find("> span");

This can't work because that is an invalid selector.  It would require 
the use of an implied simple selector which matches only the context 
node, which gets prefixed to each sequence of simple selectors within 
the selector.

i.e.
foo.querySelectorAll(">strong, em");
would have to be equivalement to:

foo.querySelectorAll("$self>strong,$self em");

where $self is some simple selector (or a sequence of simple selectors) 
that only matches the node foo.

> // DOM document.getElementById("foo").querySelectorAll(":root >
> span")
> 
> This is something that a library can easily detect and inject.

There has been much discussion of this solution in the past, using 
various pseudo-classes such as ":this", ":root" and ":scope".  AFAICT, 
this was first suggested in June 2007 on member-webapi:

| There is, however, one possible use case that isn't covered easily.
| When you want to do element.get*() and only match child nodes of that
| element, rather than all descendants.
|
| e.g. foo.getAll("?>div"); where the "?" is some simple selector that
| only matches the foo element (the root of the subtree).  It could be
| handled using an ID selector, but that doesn't work when there is no
| ID on the element.  That's one possible use case for making :root
| match the root of the subtree, but I can't justify that given its
| definition in selectors.  It would probably need a new special
| pseudo-class, but that's up to the CSSWG.

http://lists.w3.org/Archives/Member/member-webapi/2007Jun/0019.html

| Oh, that's like the issue I pointed out at the end of a previous post
| [1].  If there were significant use cases for it, I think we would
| need a special self referencing selector of some kind. e.g.
|
|   myElem.get(':this foo > bar');
|
| or maybe call it :scope or something.

http://lists.w3.org/Archives/Member/member-webapi/2007Jun/0024.html


There are several reasons the spec is defined the way it is on this issue.

1. It's how selectors work in existing Selector implementations. i.e. 
Given any element, the selector is evaluated against it in the context 
of the entire document tree. That makes it easier for implementations to 
implement this API without requiring them to significantly alter their 
selector engines.

2. This makes it more flexible for authors, so that they can select 
based upon ancestor elements if needed.

3. Introducing a :scope selector (or equivalent) solves all the issues 
you have raised, without sacrificing potentially useful functionality. 
:scope would also be useful in contexts outside of Selectors API, such 
as within HTML5's scoped stylesheets and possibly XBL.

So while you haven't convinced me that we need to significantly alter 
the spec to address this issue, you have convinced me that we should try 
to expedite the specification and implementation of a :scope selector 
(or equivalent).

> * Error-handling
> 
> try { document.querySelectorAll("div:foo"); } catch(e) { alert(e); //
> "Error: SYNTAX_ERR: DOM Exception 12" }
> 
> If there was an extra property to point to what the inappropriate
> selector was, that'd be fundamentally important. Probably the best
> solution (for both implementers and JavaScript library authors) would
> be to simply provide a character index, working something like the
> following:
> 
> var selector = "div:foo"; try { document.querySelectorAll(selector); 
> } catch(e) { alert(selector.slice(e.position)); // ":foo" }

I think Boris has raised some fairly significant issues with this 
proposed solution.  While I'm not opposed to the concept of providing 
authors with more diagnostic information about errors, the proposed 
solution seems difficult to implement and inadequate, and I'm not sure 
how to improve it.

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

Received on Friday, 2 May 2008 12:29:43 UTC