- From: Lachlan Hunt <lachlan.hunt@lachy.id.au>
- Date: Fri, 02 May 2008 14:29:02 +0200
- To: John Resig <jresig@mozilla.com>
- Cc: public-webapi@w3.org
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