Hi,
Based on the ongoing discussion, I've put together the following list
of requirements and sumarised use cases that should be met by the design
of new features in selectors API.
*Use Cases*
1. Select elements that are descendants of specific Element node, where
all elements matched by the chain of selectors are all descendants of
that element.
<section>
<h1>A</h1>
<div id="foo">
<h1>B</h1>
<section>
<h1>C</h1>
</section>
</div>
<section>
<h1>D</h1>
</section>
</section>
e.g. JQuery
$("#foo").find("section h1")
This should find heading C only.
2. Select elements that is a child of a specific Element node.
e.g.
$("#foo").find(">h1")
3. Select elements that are siblings (adjacent + or general ~) of a
specific Element node.
e.g.
$("#foo").find("+section h1")
$("#foo").find("~section h1")
4. Select elements relative to a specific Element node, conditionally
based on it's own state.
e.g. (Pseudo code)
if Element foo has class name "X",
Select descendant elements matching "input[type=radio]"
Otherwise, if Element foo has class name "Y"
Select descendant elements matching "input[type=checkbox]"
Alternatively,
foo = document.getElementById("foo");
inputs = foo.querySelectorAll(".X:scope input[type=radio],
.Y:scope input[type=checkbox]");
5. Select elements relative to a specific Element node, conditionally
based on the state of an ancestor or previous sibling element.
e.g. (Pseudo code)
if ancestor Element section has class name "X",
Select descendant elements matching "input[type=radio]"
Otherwise, if ancestor Element section has class name "Y"
Select descendant elements matching "input[type=checkbox]"
foo = document.getElementById("foo");
inputs = foo.querySelectorAll("section.X :scope input[type=radio],
section.Y :scope input[type=checkbox]");
*General*
* Method names should be short, memorable and easy to type.
- querySelectorAll is considered too long by authors.
* Methods should prioritise optimisations for the common cases over
full flexibility. Less common and more complex cases can be handled
by existing methods.
- Use cases 4 and 5 may be left to existing methods, depending on
complexity they introduce and the impact upon potential
optimisations. i.e. The cost vs. benefit of supporting them in new
methods.
* Optimisations should consider both authors and implementations
*Implicit Scoping*
* In the common case, when called on an element, selectors should be
scoped relative to the context element
* In the common case when called on the document, it either:
- Search the whole document, or
- Be scoped relative to a collection of reference elements
* Support matching descendants, next siblings and their descendants
* Should avoid matching ancestors or previous siblings
- (See reference combinator issue below)
*Syntax*
* Should not require authors to explicitly use :scope, at least when it
occurs at the beginning.
* Selectors should be able to begin with combinators, :scope should be
implied at the beginning.
*Return Value*
* Methods should return either a single Element node and/or an
collection of elements
* A collection of Elements should be
- Mutable
- Static, not live
- Support all, or at least some, methods from Array. Need to
determine which functionality is most important.
- JS Libraries use Array.slice hack to convert a NodeList to an
Array. This is a sub-optimal solution.
*Implementation Optimisations*
* It should be possible to, at least in the common cases, to limit the
set of potential matches to a subset of the document.
- This reduces the number of comparisons that need to be done and
thus time to compute the result.
* Ideally, it should be possible to determine heuristically from the
Selector whether the impelemntation needs to check:
- Only context element's descendants (descendant or child combinators)
- Only context element's siblings (sibling combinators)
- Both
Issue: What should happen with the new reference combinator?
label.find("/for/ input");
div.find("label /for/ input")
div.find(">label /for/ input + span")
* What would authors expect the result to be?
* Should these search:
- The whole document (or whole tree, if disconnected from document)?
- Only descendants?
- Only descendants and siblings?
- Other subset of the document?
* How does each option affect performance?
* Are there any possible optimisations for these cases?
e.g.
<label for="foo">Label</label>
<input type="text" name="a" id="foo">
<input type="text" name="b" id="foo">
"label /for/ input" matches both inputs, just like "#foo"
Note: If you have anything to add, please do so. I'll be on holiday and
mostly offline for the next 3 weeks. I'll review any additional
discussion and attempt to draft up a possible solution in the spec after
I get back from holidays.
--
Lachlan Hunt - Opera Software
http://lachy.id.au/
http://www.opera.com/