Re: Selector Parsing for Selectors API

On 2012-10-30 11:42, Lachlan Hunt wrote:
> Additionally, I would like to move the definition for relative selectors
> and the related parsing to Selectors 4.  This is a concept I defined in
> Selectors API Level 2, which basically allows selectors to begin with a
> combinator and have an implied :scope.
> 
> http://dev.w3.org/2006/webapi/selectors-api2/#grammar

I see that Selectors 4 has since introduced the concept of
"scope-relative selectors", which seems to be what I was asking for.
Though, there are some issues with the way it is currently written.

http://dev.w3.org/csswg/selectors4/#scoping

Firstly, the example in this section states that element.find() uses
scope-contained selectors like scoped stylesheets in HTML.  But it
doesn't. It uses scope relative selectors. These are different because
.find() needs to be able to match elements that are not descendants of
the context object.

Secondly, the current scope-relative selector definition states:

> With this method of scoping, ":scope " (the :scope pseudo-class
> followed by a space) is implied at the beginning of each complex
> selector, allowing them to begin syntactically with a combinator. The
> scoping element matches this implied :scope selector, but does not
> limit which elements match.

The find(), findAll() and matches() methods were designed with special
rules that defined when :scope should and should not be prepended to
each selector, which are not properly covered by the above definition.
These are the rules used in Selectors API:

---

1. If the relative selector begins with a combinator, then prepend the
   simple selector ":scope" to the relative selector.

2. Otherwise, if the reference nodes is an empty collection, do nothing.

3. Otherwise, if any compound selector within relative selector
   includes a functional pseudo-class that accepts a selector as its
   parameter, and which contains the ":scope" pseudo-class anywhere
   within it, then do nothing.

4. Otherwise, if the relative selector includes :scope within any
   compound or simple selector, then do nothing.

5. Otherwise, if the scope flag is set, prepend the simple selector
   ":scope" and a descendant combinator (' ') to the relative selector.

---

The 'scope flag' that is mentioned above is set or unset depending on
which method is called.  The flag is set when find() or findAll() are
called, and it is unset when matches() is called.  This is because for
.matches(), the subject of the selector needs to match the context
object, rather than anywhere else in the selector. This would not work
if :scope was automatically prepended.

To illustrate the effect of the above rules:

1. :scope is always prepended for these cases beginning with a combinator:

* document.find(">div")              // --> ":scope>div"
* document.find("~:not(:scope)")     // --> ":scope~:not(:scope)"
* document.find("+div:scope p")      // --> ":scope+div:scope p"

* element.find(">div")               // --> ":scope>div"
* element.find("~:not(:scope)")      // --> ":scope~:not(:scope)"
* element.find("+div:scope p")       // --> ":scope+div:scope p"

* element.matches(">div")            // --> ":scope>div"
* element.matches("~:not(:scope)")   // --> ":scope~:not(:scope)"
* element.matches("+div:scope p")    // --> ":scope+div:scope p"

2. :scope is not prepended for these cases where the contextual
reference element set is empty:

* document.find("div", [])           // --> "div"
* element.find("div", [])            // --> "div"
* element.matches("div", [])         // --> "div"

3. :scope is not prepended for these cases that include ':scope' within
a functional pseudo-class:

* document.find(":not(:scope)")      // --> ":not(:scope)"
* element.find(":not(:scope)")       // --> ":not(:scope)"
* element.matches(":not(:scope)")    // --> ":not(:scope)"

4. :scope is not prepended for these cases that include ':scope' as part
of any simple or compound selector:

* document.find("div:scope p")       // --> "div:scope p"
* element.find("div:scope p")        // --> "div:scope p"
* element.matches("div:scope p")     // --> "div:scope p"

5.
  a) :scope and a descendant combinator are prepended when the 'scope
flag' is set:

* document.find("div p")             // --> ":scope div p"
* document.find("div p", refNodes)   // --> ":scope div p"
* element.find("div p")              // --> ":scope div p"
* element.find("div p", refNodes)    // --> ":scope div p"

  b) :scope is not prepended when the 'scope flag' is unset:

* element.matches("div p")           // --> "div p"
* element.matches("div p", refNodes) // --> "div p"

These rules are incorporated into the selectorsapi branch of DOM
Standard, which is available on github (See Overview.src.html). These
changes aren't published anywhere yet.

https://github.com/lachlanhunt/dom/tree/selectorsapi

Note: You can still see the old Selectors API 2 draft, which includes an
older version of that algorithm. It's more of less the same, but the new
version I quoted above was just rephrased slightly.

http://dev.w3.org/2006/webapi/selectors-api2/#parse-a-relative-selector

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

Received on Monday, 14 January 2013 14:06:08 UTC