Re: QSA, the problem with :scope, and naming

2011-10-19 Thread Tab Atkins Jr.
On Wed, Oct 19, 2011 at 5:17 AM, Lachlan Hunt lachlan.h...@lachy.id.au wrote:
 1. Syntax

 In style scoped, selectors still can't begin with a combinator, but in the
 proposed API, they can.

I agree with Lachy here.  I think it's valuable to have consistency
with style scoped, so that a selector passed to el.findAll() and one
put in a style scoped that's a child of el return the same results.

You already have to explicitly add :scope if you want to do some
additional selecting of the scoping element anyway.

This breaks consistency with jQuery, but it maintains consistency with
the rest of the platform.  I think this is important enough to justify
the slight loss in terseness in the situations where you want a child
or reference combinator off of the scoping element.


 The @global at-rule was proposed to

I'll make a reasonable assumption about what Lachy was planning to say
here, and say that QSA seems to already solve the consistency with
@global within style scoped issue.  At least as far as I can tell,
it acts the same.


 2. Matching the Context Element

 In scoped stylesheets, the context element itself can be the subject of a
 selector. But the proposed API will never return the element itself in the
 result.

 div.findAll(div) // Does not match the element itself

 (same as querySelectorAll() in this case)

 div
  style scoped
    div { ... } /* Matches the context element */
  /style
 /div

While I think we should match style scoped here, I believe the
conflict should be resolved by changing style scoped.  A few people
in the last discussion preferred selectors to automatically match the
scoping element, but I still think that's a bad decision.  The scoping
element should only be returned if a selector is a single compound
selector containing :scope.  It makes selectors a little bit more
complex to understand, but in an intuitive way.

Regardless of what ends up happening in style scoped, I agree with
the API choice here to make div.find(div) not match the calling
element.  The common case is that I'm descending into the element and
wouldn't expect the calling element to match.  I'd like to write naive
algorithms that don't need to either manually check the results
against the calling element or defensively write
div.find(:not(:scope) div).  I'm okay with using the presence of
:scope in the selector as a declaration of intent here, and switch
behavior accordingly.


 3. The Subject of Selectors

 In scoped stylesheets, the potential matches of a selector will only
 include:
 * The context element itself
 * Descendants of the context element

 In the proposed API, the potential matches will include:
 * Descendants of the context element
 * Siblings of the context element

 In the existing API, the potential matches include:
 * Descendants of the context element only


 div.findAll(+p) // Matches sibling p elements

 div.querySelectorAll(:scope+p) // Matches nothing
 document.querySelectorAll(:scope+p, div) // Matches sibling p elements

 div
  style scoped
    :scope+p { ... } /* Matches nothing */
  /style
 div
 p.../p

I am okay with this behavioral split from style scoped, and believe
it's both useful and intuitive.

(Note that the function can actually return elements from *anywhere*
given the current Selectors 4 draft, as it can follow a reference
combinator which can point to an arbitrary position in the doc.)

~TJ



Re: QSA, the problem with :scope, and naming

2011-10-19 Thread Jonas Sicking
On Tue, Oct 18, 2011 at 9:42 AM, Alex Russell slightly...@google.com wrote:
 Lachlan and I have been having an...um...*spirited* twitter discussion
 regarding querySelectorAll, the (deceased?) queryScopedSelectorAll,
 and :scope. He asked me to continue here, so I'll try to keep it
 short:

 The rooted forms of querySelector and querySelectorAll are mis-designed.

 Discussions about a Scoped variant or :scope pseudo tacitly
 acknowledge this, and the JS libraries are proof in their own right:
 no major JS library exposes the QSA semantic, instead choosing to
 implement a rooted search.

 Related and equally important, that querySelector and querySelectorAll
 are often referred to by the abbreviation QSA suggests that its name
 is bloated and improved versions should have shorter names. APIs gain
 use both through naming and through use. On today's internet -- the
 one where 50% of all websites include jQuery -- you could even go with
 element.$(selector) and everyone would know what you mean: it's
 clearly a search rooted at the element on the left-hand side of the
 dot.

 Ceteris peribus, shorter is better. When there's a tie that needs to
 be broken, the more frequently used the API, the shorter the name it
 deserves -- i.e., the larger the component of its meaning it will gain
 through use and repetition and not naming and documentation.

 I know some on this list might disagree, but all of the above is
 incredibly non-controversial today. Even if there may have been
 debates about scoping or naming when QSA was originally designed,
 history has settled them. And QSA lost on both counts.

 I therefore believe that this group's current design for scoped
 selection could be improved significantly. If I understand the latest
 draft (http://www.w3.org/TR/selectors-api2/#the-scope-pseudo-class)
 correctly, a scoped search for multiple elements would be written as:

   element.querySelectorAll(:scope  div  .thinger);

 Both then name and the need to specify :scope are punitive to
 readers and writers of this code. The selector is *obviously*
 happening in relationship to element somehow. The only sane
 relationship (from a modern JS hacker's perspective) is that it's
 where our selector starts from. I'd like to instead propose that we
 shorten all of this up and kill both stones by introducing a new API
 pair, find and findAll, that are rooted as JS devs expect. The
 above becomes:

   element.findAll( div  .thinger);

 Out come the knives! You can't start a selector with a combinator!

 Ah, but we don't need to care what CSS thinks of our DOM-only API. We
 can live and let live by building on :scope and specifying find* as
 syntactic sugar, defined as:

  HTMLDocument.prototype.find =
  HTMLElement.prototype.find = function(rootedSelector) {
     return this.querySelector(:scope  + rootedSelector);
   }

   HTMLDocument.prototype.findAll =
   HTMLElement.prototype.findAll = function(rootedSelector) {
     return this.querySelectorAll(:scope  + rootedSelector);
   }

 Of course, :scope in this case is just a special case of the ID
 rooting hack, but if we're going to have it, we can kill both birds
 with it.

 Obvious follow up questions:

 Q.) Why do we need this at all? Don't the toolkits already just do
 this internally?
 A.) Are you saying everyone, everywhere, all the time should need to
 use a toolkit to get sane behavior from the DOM? If so, what are we
 doing here, exactly?

 Q.) Shorter names? Those are for weaklings!
 A.) And humans. Who still constitute most of our developers. Won't
 someone please think of the humans?

 Q.) You're just duplicating things!
 A.) If you ignore all of the things that are different, then that's
 true. If not, well, then no. This is a change. And a good one for the
 reasons listed above.

 Thoughts?

I like the general idea here. And since we're changing behavior, I
think it's a good opportunity to come up with shorter names. Naming is
really hard. The shorter names we use, the more likely it is that
we're going to break webpages which are messing around with the
prototype chain and it increases the risk that we'll regret it later
when we come up with even better functions which should use those
names. Say that we come up with an even better query language than
selectors, at that point .find will simply not be available to us.

However, it does seem like selectors are here to stay. And as much as
they have shortcomings, people seem to really like them for querying.

So with that out of the way, I agree that the CSS working group
shouldn't be what is holding us back. However we do need a precise
definition of what the new function does. Is prepending :scope  and
then parsing as a normal selector always going to give the behavior we
want? This is actually what I think we got stuck on when the original
querySelector was designed.

So let's get into specifics about how things should work. According to
your proposal of simply prepending a conceptual :scope to each
selector 

Re: QSA, the problem with :scope, and naming

2011-10-19 Thread Boris Zbarsky

On 10/19/11 10:07 PM, Jonas Sicking wrote:

I like the general idea here. And since we're changing behavior, I
think it's a good opportunity to come up with shorter names. Naming is
really hard. The shorter names we use, the more likely it is that
we're going to break webpages which are messing around with the
prototype chain


Not just the proto chain.  Every method you add on Element or Document 
will break any inline event handler attributes that happen to use that 
name as a bareword.  We had some amount of that with the list property 
on inputs, and that only added a property on HTMLInputElement


Again, in this case a shorter name may make sense, but in general there 
are good reasons for not using short names all the time.


-Boris




Re: QSA, the problem with :scope, and naming

2011-10-19 Thread Tab Atkins Jr.
On Wed, Oct 19, 2011 at 7:22 PM, Ojan Vafai o...@chromium.org wrote:
 On Wed, Oct 19, 2011 at 7:07 PM, Jonas Sicking jo...@sicking.cc wrote:
 .findAll(body  :scope  div)  // returns nothing

 Wouldn't this return ids 1,2,3 if we're not prepending :scope as you say
 below?

Yes, but he was answering those questions based on the assumption of
always prepending :scope.


 Additionally it seems to me that we could allow the same syntax for
 style scoped. But maybe others disagree?

 Sounds good to me. A sticky case you left out is parent, sibling and
 reference combinators.
 .findAll(+ div)
 Assuming the context node has siblings, should that return them? If so,
 should it match siblings when using style scoped.
 IMO, it shouldn't match anything in either case. We should assert that
 only descendants of the scope element will ever be returned. This would also
 make it naturally match style scoped where only descendants of the scope
 element are ever affected.

I disagree.  It's extremely useful and natural for .find(:scope +
div) to match sibling of the context node.  Basically, the presence
of :scope would turn off *all* the limitations; the only thing that
the context node still does is match the :scope pseudo.  The selector
should match across and return elements from anywhere in the document.

This is where I think that .find and style scoped should diverge in behavior.

.find should have two cases:

1. Selector without :scope - run the selector only across the
descendants of the context node.  (No need to explicitly filter, since
the results will only contain descendants of the context node
already.)
2. Selector with :scope - run the selector across the entire document,
with :scope matching the context node.  (No filtering here, either.)

style scoped should (I think) have three cases:

1. Selector without :scope - same as .find
2. Selector with :scope - Same as #1, but also including the context node.
3. Selector in @global - run the selector across the entire document,
filter the results to only be the context node and its descendants.

(Some people disagree with me on this, and think that #1 and #2 should
be merged to always include the context node.  That's acceptable, but
I don't like it as much.)

I think it's perfectly okay that these two APIs have different cases.

~TJ



Re: QSA, the problem with :scope, and naming

2011-10-19 Thread Jonas Sicking
On Wed, Oct 19, 2011 at 7:22 PM, Ojan Vafai o...@chromium.org wrote:
 On Wed, Oct 19, 2011 at 7:07 PM, Jonas Sicking jo...@sicking.cc wrote:

 On Tue, Oct 18, 2011 at 9:42 AM, Alex Russell slightly...@google.com
 wrote:
  Lachlan and I have been having an...um...*spirited* twitter discussion
  regarding querySelectorAll, the (deceased?) queryScopedSelectorAll,
  and :scope. He asked me to continue here, so I'll try to keep it
  short:
 
  The rooted forms of querySelector and querySelectorAll are
  mis-designed.
 
  Discussions about a Scoped variant or :scope pseudo tacitly
  acknowledge this, and the JS libraries are proof in their own right:
  no major JS library exposes the QSA semantic, instead choosing to
  implement a rooted search.
 
  Related and equally important, that querySelector and querySelectorAll
  are often referred to by the abbreviation QSA suggests that its name
  is bloated and improved versions should have shorter names. APIs gain
  use both through naming and through use. On today's internet -- the
  one where 50% of all websites include jQuery -- you could even go with
  element.$(selector) and everyone would know what you mean: it's
  clearly a search rooted at the element on the left-hand side of the
  dot.
 
  Ceteris peribus, shorter is better. When there's a tie that needs to
  be broken, the more frequently used the API, the shorter the name it
  deserves -- i.e., the larger the component of its meaning it will gain
  through use and repetition and not naming and documentation.
 
  I know some on this list might disagree, but all of the above is
  incredibly non-controversial today. Even if there may have been
  debates about scoping or naming when QSA was originally designed,
  history has settled them. And QSA lost on both counts.
 
  I therefore believe that this group's current design for scoped
  selection could be improved significantly. If I understand the latest
  draft (http://www.w3.org/TR/selectors-api2/#the-scope-pseudo-class)
  correctly, a scoped search for multiple elements would be written as:
 
    element.querySelectorAll(:scope  div  .thinger);
 
  Both then name and the need to specify :scope are punitive to
  readers and writers of this code. The selector is *obviously*
  happening in relationship to element somehow. The only sane
  relationship (from a modern JS hacker's perspective) is that it's
  where our selector starts from. I'd like to instead propose that we
  shorten all of this up and kill both stones by introducing a new API
  pair, find and findAll, that are rooted as JS devs expect. The
  above becomes:
 
    element.findAll( div  .thinger);
 
  Out come the knives! You can't start a selector with a combinator!
 
  Ah, but we don't need to care what CSS thinks of our DOM-only API. We
  can live and let live by building on :scope and specifying find* as
  syntactic sugar, defined as:
 
   HTMLDocument.prototype.find =
   HTMLElement.prototype.find = function(rootedSelector) {
      return this.querySelector(:scope  + rootedSelector);
    }
 
    HTMLDocument.prototype.findAll =
    HTMLElement.prototype.findAll = function(rootedSelector) {
      return this.querySelectorAll(:scope  + rootedSelector);
    }
 
  Of course, :scope in this case is just a special case of the ID
  rooting hack, but if we're going to have it, we can kill both birds
  with it.
 
  Obvious follow up questions:
 
  Q.) Why do we need this at all? Don't the toolkits already just do
  this internally?
  A.) Are you saying everyone, everywhere, all the time should need to
  use a toolkit to get sane behavior from the DOM? If so, what are we
  doing here, exactly?
 
  Q.) Shorter names? Those are for weaklings!
  A.) And humans. Who still constitute most of our developers. Won't
  someone please think of the humans?
 
  Q.) You're just duplicating things!
  A.) If you ignore all of the things that are different, then that's
  true. If not, well, then no. This is a change. And a good one for the
  reasons listed above.
 
  Thoughts?

 I like the general idea here. And since we're changing behavior, I
 think it's a good opportunity to come up with shorter names. Naming is
 really hard. The shorter names we use, the more likely it is that
 we're going to break webpages which are messing around with the
 prototype chain and it increases the risk that we'll regret it later
 when we come up with even better functions which should use those
 names. Say that we come up with an even better query language than
 selectors, at that point .find will simply not be available to us.

 However, it does seem like selectors are here to stay. And as much as
 they have shortcomings, people seem to really like them for querying.

 So with that out of the way, I agree that the CSS working group
 shouldn't be what is holding us back. However we do need a precise
 definition of what the new function does. Is prepending :scope  and
 then parsing as a normal selector always going to give the 

Re: QSA, the problem with :scope, and naming

2011-10-19 Thread Jonas Sicking
On Wed, Oct 19, 2011 at 10:08 PM, Tab Atkins Jr. jackalm...@gmail.com wrote:
 On Wed, Oct 19, 2011 at 7:22 PM, Ojan Vafai o...@chromium.org wrote:
 On Wed, Oct 19, 2011 at 7:07 PM, Jonas Sicking jo...@sicking.cc wrote:
 .findAll(body  :scope  div)  // returns nothing

 Wouldn't this return ids 1,2,3 if we're not prepending :scope as you say
 below?

 Yes, but he was answering those questions based on the assumption of
 always prepending :scope.

Exactly.

 Additionally it seems to me that we could allow the same syntax for
 style scoped. But maybe others disagree?

 Sounds good to me. A sticky case you left out is parent, sibling and
 reference combinators.
 .findAll(+ div)
 Assuming the context node has siblings, should that return them? If so,
 should it match siblings when using style scoped.
 IMO, it shouldn't match anything in either case. We should assert that
 only descendants of the scope element will ever be returned. This would also
 make it naturally match style scoped where only descendants of the scope
 element are ever affected.

 I disagree.  It's extremely useful and natural for .find(:scope +
 div) to match sibling of the context node.  Basically, the presence
 of :scope would turn off *all* the limitations; the only thing that
 the context node still does is match the :scope pseudo.  The selector
 should match across and return elements from anywhere in the document.

 This is where I think that .find and style scoped should diverge in 
 behavior.

 .find should have two cases:

 1. Selector without :scope - run the selector only across the
 descendants of the context node.  (No need to explicitly filter, since
 the results will only contain descendants of the context node
 already.)
 2. Selector with :scope - run the selector across the entire document,
 with :scope matching the context node.  (No filtering here, either.)

 style scoped should (I think) have three cases:

 1. Selector without :scope - same as .find
 2. Selector with :scope - Same as #1, but also including the context node.
 3. Selector in @global - run the selector across the entire document,
 filter the results to only be the context node and its descendants.

 (Some people disagree with me on this, and think that #1 and #2 should
 be merged to always include the context node.  That's acceptable, but
 I don't like it as much.)

 I think it's perfectly okay that these two APIs have different cases.

I'm not sure I understand what you are proposing here. Are you saying that

div
style scoped
:scope {
  background: green;
}
/style
/div

should set the background of the div green? This does seem intuitive
I agree, but it might also lead to strange behavior since the
rendering of the div will change once the stylesheet is parsed. In
other words, it's very easy to get flash-of-unstyled-content behavior.

/ Jonas



Re: QSA, the problem with :scope, and naming

2011-10-19 Thread Jonas Sicking
On Tue, Oct 18, 2011 at 9:42 AM, Alex Russell slightly...@google.com wrote:
 Lachlan and I have been having an...um...*spirited* twitter discussion
 regarding querySelectorAll, the (deceased?) queryScopedSelectorAll,
 and :scope. He asked me to continue here, so I'll try to keep it
 short:

 The rooted forms of querySelector and querySelectorAll are mis-designed.

 Discussions about a Scoped variant or :scope pseudo tacitly
 acknowledge this, and the JS libraries are proof in their own right:
 no major JS library exposes the QSA semantic, instead choosing to
 implement a rooted search.

 Related and equally important, that querySelector and querySelectorAll
 are often referred to by the abbreviation QSA suggests that its name
 is bloated and improved versions should have shorter names. APIs gain
 use both through naming and through use. On today's internet -- the
 one where 50% of all websites include jQuery -- you could even go with
 element.$(selector) and everyone would know what you mean: it's
 clearly a search rooted at the element on the left-hand side of the
 dot.

 Ceteris peribus, shorter is better. When there's a tie that needs to
 be broken, the more frequently used the API, the shorter the name it
 deserves -- i.e., the larger the component of its meaning it will gain
 through use and repetition and not naming and documentation.

 I know some on this list might disagree, but all of the above is
 incredibly non-controversial today. Even if there may have been
 debates about scoping or naming when QSA was originally designed,
 history has settled them. And QSA lost on both counts.

 I therefore believe that this group's current design for scoped
 selection could be improved significantly. If I understand the latest
 draft (http://www.w3.org/TR/selectors-api2/#the-scope-pseudo-class)
 correctly, a scoped search for multiple elements would be written as:

   element.querySelectorAll(:scope  div  .thinger);

 Both then name and the need to specify :scope are punitive to
 readers and writers of this code. The selector is *obviously*
 happening in relationship to element somehow. The only sane
 relationship (from a modern JS hacker's perspective) is that it's
 where our selector starts from. I'd like to instead propose that we
 shorten all of this up and kill both stones by introducing a new API
 pair, find and findAll, that are rooted as JS devs expect. The
 above becomes:

   element.findAll( div  .thinger);

 Out come the knives! You can't start a selector with a combinator!

 Ah, but we don't need to care what CSS thinks of our DOM-only API. We
 can live and let live by building on :scope and specifying find* as
 syntactic sugar, defined as:

  HTMLDocument.prototype.find =
  HTMLElement.prototype.find = function(rootedSelector) {
     return this.querySelector(:scope  + rootedSelector);
   }

   HTMLDocument.prototype.findAll =
   HTMLElement.prototype.findAll = function(rootedSelector) {
     return this.querySelectorAll(:scope  + rootedSelector);
   }

 Of course, :scope in this case is just a special case of the ID
 rooting hack, but if we're going to have it, we can kill both birds
 with it.

 Obvious follow up questions:

 Q.) Why do we need this at all? Don't the toolkits already just do
 this internally?
 A.) Are you saying everyone, everywhere, all the time should need to
 use a toolkit to get sane behavior from the DOM? If so, what are we
 doing here, exactly?

 Q.) Shorter names? Those are for weaklings!
 A.) And humans. Who still constitute most of our developers. Won't
 someone please think of the humans?

 Q.) You're just duplicating things!
 A.) If you ignore all of the things that are different, then that's
 true. If not, well, then no. This is a change. And a good one for the
 reasons listed above.

 Thoughts?

Oh, and as a separate issue. I think .findAll should return a plain
old JS Array. Not a NodeList or any other type of host object. One of
the use cases is being able to mutate the returned value. This is
useful if you're for example doing multiple .findAll calls (possibly
with different context nodes) and want to merge the resulting lists
into a single list.

/ Jonas



Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Alex Russell
On Tue, Oct 18, 2011 at 5:42 PM, Alex Russell slightly...@google.com wrote:
 Lachlan and I have been having an...um...*spirited* twitter discussion
 regarding querySelectorAll, the (deceased?) queryScopedSelectorAll,
 and :scope. He asked me to continue here, so I'll try to keep it
 short:

 The rooted forms of querySelector and querySelectorAll are mis-designed.

 Discussions about a Scoped variant or :scope pseudo tacitly
 acknowledge this, and the JS libraries are proof in their own right:
 no major JS library exposes the QSA semantic, instead choosing to
 implement a rooted search.

 Related and equally important, that querySelector and querySelectorAll
 are often referred to by the abbreviation QSA suggests that its name
 is bloated and improved versions should have shorter names. APIs gain
 use both through naming and through use.

Sorry, this should say meaning. APIs gain *meaning* through both use
and naming.

 On today's internet -- the
 one where 50% of all websites include jQuery -- you could even go with
 element.$(selector) and everyone would know what you mean: it's
 clearly a search rooted at the element on the left-hand side of the
 dot.

 Ceteris peribus, shorter is better. When there's a tie that needs to
 be broken, the more frequently used the API, the shorter the name it
 deserves -- i.e., the larger the component of its meaning it will gain
 through use and repetition and not naming and documentation.

 I know some on this list might disagree, but all of the above is
 incredibly non-controversial today. Even if there may have been
 debates about scoping or naming when QSA was originally designed,
 history has settled them. And QSA lost on both counts.

 I therefore believe that this group's current design for scoped
 selection could be improved significantly. If I understand the latest
 draft (http://www.w3.org/TR/selectors-api2/#the-scope-pseudo-class)
 correctly, a scoped search for multiple elements would be written as:

   element.querySelectorAll(:scope  div  .thinger);

 Both then name and the need to specify :scope are punitive to
 readers and writers of this code. The selector is *obviously*
 happening in relationship to element somehow. The only sane
 relationship (from a modern JS hacker's perspective) is that it's
 where our selector starts from. I'd like to instead propose that we
 shorten all of this up and kill both stones by introducing a new API
 pair, find and findAll, that are rooted as JS devs expect. The
 above becomes:

   element.findAll( div  .thinger);

 Out come the knives! You can't start a selector with a combinator!

 Ah, but we don't need to care what CSS thinks of our DOM-only API. We
 can live and let live by building on :scope and specifying find* as
 syntactic sugar, defined as:

  HTMLDocument.prototype.find =
  HTMLElement.prototype.find = function(rootedSelector) {
     return this.querySelector(:scope  + rootedSelector);
   }

   HTMLDocument.prototype.findAll =
   HTMLElement.prototype.findAll = function(rootedSelector) {
     return this.querySelectorAll(:scope  + rootedSelector);
   }

 Of course, :scope in this case is just a special case of the ID
 rooting hack, but if we're going to have it, we can kill both birds
 with it.

 Obvious follow up questions:

 Q.) Why do we need this at all? Don't the toolkits already just do
 this internally?
 A.) Are you saying everyone, everywhere, all the time should need to
 use a toolkit to get sane behavior from the DOM? If so, what are we
 doing here, exactly?

 Q.) Shorter names? Those are for weaklings!
 A.) And humans. Who still constitute most of our developers. Won't
 someone please think of the humans?

 Q.) You're just duplicating things!
 A.) If you ignore all of the things that are different, then that's
 true. If not, well, then no. This is a change. And a good one for the
 reasons listed above.

 Thoughts?




Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Erik Arvidsson
On Tue, Oct 18, 2011 at 09:42, Alex Russell slightly...@google.com wrote:
 Ah, but we don't need to care what CSS thinks of our DOM-only API. We
 can live and let live by building on :scope and specifying find* as
 syntactic sugar, defined as:

  HTMLDocument.prototype.find =
  HTMLElement.prototype.find = function(rootedSelector) {
     return this.querySelector(:scope  + rootedSelector);
   }

   HTMLDocument.prototype.findAll =
   HTMLElement.prototype.findAll = function(rootedSelector) {
     return this.querySelectorAll(:scope  + rootedSelector);
   }

I like the way you think. Can I subscribe to your mailing list?

One thing to point out with the desugar is that it has a bug and most
JS libs have the same but. querySelectorAll allows multiple selectors,
separated by a comma and to do this correctly you need to parse the
selector which of course requires tons of code so no one does this.
Lets fix that by building this into the platform.

-- 
erik



Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Brian Kardell
 This is _very_ hard to reasonably unless the browser can trust those
 functions to not do anything weird.  Which of course it can't.  So your
 options are either much slower selector matching or not having this. Your
 pick.

This too has come up in some discussions on CSS (CSSOM I think) that I
have had.  In the right context - I don't think it would actually be
that hard.  It would require a way to provide a sand-boxed evaluation
(read only elements) and a pattern much like jquery's where it is a
filter which can only return true or false.  True enough that it would
be slower than native for a few reasons - but perhaps still useful.


On Tue, Oct 18, 2011 at 4:40 PM, Boris Zbarsky bzbar...@mit.edu wrote:
 On 10/18/11 4:20 PM, Yehuda Katz wrote:

  * Speeding up certain operations like `#foo` and `body`. There is *no
    excuse* for it being possible to implement userland hacks that
    improve on the performance of querySelectorAll.

 Sure there is.  One such excuse, for example, is that the userland hacks
 have different behavior from querySelectorAll in many cases.  Now the author
 happens to know that the difference doesn't matter in their case, but the
 _browser_ has no way to know that.

 The other excuse is that adding special cases (which is what you're asking
 for) slows down all the non-special-case codepaths.  That may be fine for
 _your_ usage of querySelectorAll, where you use it with a particular limited
 set of selectors, but it's not obvious that this is always a win.

 This may be the result of browsers failing to cache the result of parsing
 selectors

 Yep.  Browsers don't cache it.  There's generally no reason to.  I have yet
 to see any real-life testcase bottlenecked on this part of querySelectorAll
 performance.

    or something else, but the fact remains that qSA can be noticably
    slower than the old DOM methods, even when Sizzle needs to parse the
    selector to look for fast-paths.

 I'd love to see testcases showing this.

 jQuery also handles certain custom pseudoselectors, and it might be nice
 if it was possible to register JavaScript functions that qSA would use
 if it found an unknown pseudo

 This is _very_ hard to reasonably unless the browser can trust those
 functions to not do anything weird.  Which of course it can't.  So your
 options are either much slower selector matching or not having this. Your
 pick.

 -Boris





Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Boris Zbarsky

On 10/18/11 5:01 PM, Brian Kardell wrote:

This too has come up in some discussions on CSS (CSSOM I think) that I
have had.  In the right context - I don't think it would actually be
that hard.  It would require a way to provide a sand-boxed evaluation
(read only elements)


This is not that easy.  Especially because you can reach all DOM objects 
from elements, so you have to lock down the entire API somehow.



and a pattern much like jquery's where it is a
filter which can only return true or false.  True enough that it would
be slower than native for a few reasons - but perhaps still useful.


The slowness comes from not having a way to tell whether the world has 
changed under you or not and therefore having to assume that it has, not 
from the actual call into JS per se.


-Boris



Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Brian Kardell
On Tue, Oct 18, 2011 at 5:04 PM, Boris Zbarsky bzbar...@mit.edu wrote:
 On 10/18/11 5:01 PM, Brian Kardell wrote:

 This too has come up in some discussions on CSS (CSSOM I think) that I
 have had.  In the right context - I don't think it would actually be
 that hard.  It would require a way to provide a sand-boxed evaluation
 (read only elements)

 This is not that easy.  Especially because you can reach all DOM objects
 from elements, so you have to lock down the entire API somehow.

Right, you would need essentially, to pass in a node list which
iterated 'lite' read-only elements.  Not impossible to imagine -
right? Maybe I'm way off, but actually seems not that difficult to
imagine the implementation.


 and a pattern much like jquery's where it is a
 filter which can only return true or false.  True enough that it would
 be slower than native for a few reasons - but perhaps still useful.

 The slowness comes from not having a way to tell whether the world has
 changed under you or not and therefore having to assume that it has, not
 from the actual call into JS per se.

I imagine that they would be implemented as filters so if you had

div .x:foo(.bar) span

The normal CSS resolution would be to get the spans, narrow by .x's
then throw what you have so far to the filter, removing anything that
returned false and carrying on as normal. The slowness as I see it
would be that the filter would yes, call across the boundary and yes
have to build some intermediate and evaluating anything too complex in
the filter in that would be very slow by comparison probably - but you
don't have to do much to be useful...  Is there something in that
pattern that I am missing in terms of  what you are saying about
identifying what has changed out from underneath you? As far as I can
see it doesn't invalidate anything that already exists in CSS/selector
implementations in terms of indexes or anything - but I've been
looking for an answer to this exact question so if you know something
I'd be very interested in even a pointer to some code so I can
understand myself.



Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Boris Zbarsky

On 10/18/11 5:23 PM, Brian Kardell wrote:

This is not that easy.  Especially because you can reach all DOM objects
from elements, so you have to lock down the entire API somehow.


Right, you would need essentially, to pass in a node list which
iterated 'lite' read-only elements.


So the script would not get an actual DOM tree and not run in the Window 
scope?  The objects would not have an ownerDocument?  What other 
restrictions would they need to have?



Maybe I'm way off, but actually seems not that difficult to
imagine the implementation.


If we're willing to pass in some totally-not-DOM data structure and run 
in some sandbox scope, then sure.



div .x:foo(.bar) span

The normal CSS resolution would be to get the spans, narrow by .x's
then throw what you have so far to the filter, removing anything that
returned false and carrying on as normal.


Normal CSS selector examines the .x part for each span as it finds it. 
Otherwise selectors like #foo  * would require building up a list of 
all elements in the DOM, no?



The slowness as I see it would be that the filter would yes, call across the 
boundary and yes
have to build some intermediate and evaluating anything too complex in
the filter in that would be very slow by comparison probably - but you
don't have to do much to be useful...  Is there something in that
pattern that I am missing in terms of  what you are saying about
identifying what has changed out from underneath you?


_If_ the filter runs JS that can touch the DOM, then in your example for 
every span you find you'd end up calling into the filter, and then you 
have to worry about the filter rearranging the DOM under you.



As far as I can see it doesn't invalidate anything that already exists in 
CSS/selector
implementations in terms of indexes or anything


At least the querySelectorAll implementations I have looked at (WebKit 
and Gecko) traverse the DOM and for each element they find check whether 
it matches the selector.  If so, they add it to the result set. 
Furthermore, selector matching itself has to walk over the tree in 
various ways (e.g. to handle combinators).  Both operations right now 
assume that the tree does NOT mutate while this is happening.


-Boris




Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Boris Zbarsky

On 10/18/11 5:23 PM, Brian Kardell wrote:

This is not that easy.  Especially because you can reach all DOM objects
from elements, so you have to lock down the entire API somehow.


Right, you would need essentially, to pass in a node list which
iterated 'lite' read-only elements.


So the script would not get an actual DOM tree and not run in the Window 
scope?  The objects would not have an ownerDocument?  What other 
restrictions would they need to have?



Maybe I'm way off, but actually seems not that difficult to
imagine the implementation.


If we're willing to pass in some totally-not-DOM data structure and run 
in some sandbox scope, then sure.



div .x:foo(.bar) span

The normal CSS resolution would be to get the spans, narrow by .x's
then throw what you have so far to the filter, removing anything that
returned false and carrying on as normal.


Normal CSS selector examines the .x part for each span as it finds it. 
Otherwise selectors like #foo  * would require building up a list of 
all elements in the DOM, no?



The slowness as I see it would be that the filter would yes, call across the 
boundary and yes
have to build some intermediate and evaluating anything too complex in
the filter in that would be very slow by comparison probably - but you
don't have to do much to be useful...  Is there something in that
pattern that I am missing in terms of  what you are saying about
identifying what has changed out from underneath you?


_If_ the filter runs JS that can touch the DOM, then in your example for 
every span you find you'd end up calling into the filter, and then you 
have to worry about the filter rearranging the DOM under you.



As far as I can see it doesn't invalidate anything that already exists in 
CSS/selector
implementations in terms of indexes or anything


At least the querySelectorAll implementations I have looked at (WebKit 
and Gecko) traverse the DOM and for each element they find check whether 
it matches the selector.  If so, they add it to the result set. 
Furthermore, selector matching itself has to walk over the tree in 
various ways (e.g. to handle combinators).  Both operations right now 
assume that the tree does NOT mutate while this is happening.


-Boris




Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Brian Kardell
On Tue, Oct 18, 2011 at 5:32 PM, Boris Zbarsky bzbar...@mit.edu wrote:
 On 10/18/11 5:23 PM, Brian Kardell wrote:

 This is not that easy.  Especially because you can reach all DOM objects
 from elements, so you have to lock down the entire API somehow.

 Right, you would need essentially, to pass in a node list which
 iterated 'lite' read-only elements.

 So the script would not get an actual DOM tree and not run in the Window
 scope?  The objects would not have an ownerDocument?  What other
 restrictions would they need to have?

They would run in their own sandbox and they would have access to the
parameters passed into the function by way of pattern.  I think that
that pattern would look a lot like jquery's selector plugin pattern
something like: The match itself, the index of the match, the
arguments to the selector itself. The 'match' in this case wouldn't be
a mutable DOM element.  You can give it a smaller API by saying that
the 'lite' version of the element that is passed in has no properties
which might give you something mutable - or you can say that all
methods/properties would also return immutable shadows of themselves.
I would be happy to walk through more detailed ideas in terms of what
specifically that would look like if there were some kind of initial
yeah, that might work - its worth looking into some more :)


 Maybe I'm way off, but actually seems not that difficult to
 imagine the implementation.

 If we're willing to pass in some totally-not-DOM data structure and run in
 some sandbox scope, then sure.

 div .x:foo(.bar) span

 The normal CSS resolution would be to get the spans, narrow by .x's
 then throw what you have so far to the filter, removing anything that
 returned false and carrying on as normal.

 Normal CSS selector examines the .x part for each span as it finds it.
 Otherwise selectors like #foo  * would require building up a list of all
 elements in the DOM, no?

I'm not sure that I understand the distinction of what you are saying
here or if it matters.  My understanding of the webkit code was that
it walks the tree (or subtree) once (as created/modifed) and optimizes
fastpath indexes on classes, ids and tags (also some other
optimizations for some slightly more complex things if I recall).  I
would have expected the querySelector** stuff to re-use that
underlying code, but I don't know - it sounds like you are saying
maybe not.


 The slowness as I see it would be that the filter would yes, call across
 the boundary and yes
 have to build some intermediate and evaluating anything too complex in
 the filter in that would be very slow by comparison probably - but you
 don't have to do much to be useful...  Is there something in that
 pattern that I am missing in terms of  what you are saying about
 identifying what has changed out from underneath you?

 _If_ the filter runs JS that can touch the DOM, then in your example for
 every span you find you'd end up calling into the filter, and then you have
 to worry about the filter rearranging the DOM under you.

 As far as I can see it doesn't invalidate anything that already exists in
 CSS/selector
 implementations in terms of indexes or anything

 At least the querySelectorAll implementations I have looked at (WebKit and
 Gecko) traverse the DOM and for each element they find check whether it
 matches the selector.  If so, they add it to the result set. Furthermore,
 selector matching itself has to walk over the tree in various ways (e.g. to
 handle combinators).  Both operations right now assume that the tree does
 NOT mutate while this is happening.

Yes - it absolutely can NOT mutate while this is happening, but it
shouldn't right?  It would be kind of non-sensical if it did.  It
doesn't have to mutate in order to be useful - even in jQuery's model,
its purpose is in order to determine what _should_ mutate, not to do
the mutation itself.


 -Boris





Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Matt Shulman
I think the query selector functionality is important enough that one
could easily justify adding additional APIs to make this work
better/faster, even if they overlap with existing APIs.  But, it would
be unfortunate if more APIs were added to the DOM and libraries still
weren't able to use them because the semantics didn't end up being
quite right.
It seems like the right approach would be to take jquery and rewrite
it to use this new API and then see empirically whether it gives the
same selection behavior as before and see how much of a performance or
simplicity gain there is after doing this.
(I think it's a good thing to allow selectors to start with
combinators.  That seems very useful.)

On Tue, Oct 18, 2011 at 9:47 AM, Alex Russell slightly...@google.com wrote:
 On Tue, Oct 18, 2011 at 5:42 PM, Alex Russell slightly...@google.com wrote:
 Lachlan and I have been having an...um...*spirited* twitter discussion
 regarding querySelectorAll, the (deceased?) queryScopedSelectorAll,
 and :scope. He asked me to continue here, so I'll try to keep it
 short:

 The rooted forms of querySelector and querySelectorAll are mis-designed.

 Discussions about a Scoped variant or :scope pseudo tacitly
 acknowledge this, and the JS libraries are proof in their own right:
 no major JS library exposes the QSA semantic, instead choosing to
 implement a rooted search.

 Related and equally important, that querySelector and querySelectorAll
 are often referred to by the abbreviation QSA suggests that its name
 is bloated and improved versions should have shorter names. APIs gain
 use both through naming and through use.

 Sorry, this should say meaning. APIs gain *meaning* through both use
 and naming.

 On today's internet -- the
 one where 50% of all websites include jQuery -- you could even go with
 element.$(selector) and everyone would know what you mean: it's
 clearly a search rooted at the element on the left-hand side of the
 dot.

 Ceteris peribus, shorter is better. When there's a tie that needs to
 be broken, the more frequently used the API, the shorter the name it
 deserves -- i.e., the larger the component of its meaning it will gain
 through use and repetition and not naming and documentation.

 I know some on this list might disagree, but all of the above is
 incredibly non-controversial today. Even if there may have been
 debates about scoping or naming when QSA was originally designed,
 history has settled them. And QSA lost on both counts.

 I therefore believe that this group's current design for scoped
 selection could be improved significantly. If I understand the latest
 draft (http://www.w3.org/TR/selectors-api2/#the-scope-pseudo-class)
 correctly, a scoped search for multiple elements would be written as:

   element.querySelectorAll(:scope  div  .thinger);

 Both then name and the need to specify :scope are punitive to
 readers and writers of this code. The selector is *obviously*
 happening in relationship to element somehow. The only sane
 relationship (from a modern JS hacker's perspective) is that it's
 where our selector starts from. I'd like to instead propose that we
 shorten all of this up and kill both stones by introducing a new API
 pair, find and findAll, that are rooted as JS devs expect. The
 above becomes:

   element.findAll( div  .thinger);

 Out come the knives! You can't start a selector with a combinator!

 Ah, but we don't need to care what CSS thinks of our DOM-only API. We
 can live and let live by building on :scope and specifying find* as
 syntactic sugar, defined as:

  HTMLDocument.prototype.find =
  HTMLElement.prototype.find = function(rootedSelector) {
     return this.querySelector(:scope  + rootedSelector);
   }

   HTMLDocument.prototype.findAll =
   HTMLElement.prototype.findAll = function(rootedSelector) {
     return this.querySelectorAll(:scope  + rootedSelector);
   }

 Of course, :scope in this case is just a special case of the ID
 rooting hack, but if we're going to have it, we can kill both birds
 with it.

 Obvious follow up questions:

 Q.) Why do we need this at all? Don't the toolkits already just do
 this internally?
 A.) Are you saying everyone, everywhere, all the time should need to
 use a toolkit to get sane behavior from the DOM? If so, what are we
 doing here, exactly?

 Q.) Shorter names? Those are for weaklings!
 A.) And humans. Who still constitute most of our developers. Won't
 someone please think of the humans?

 Q.) You're just duplicating things!
 A.) If you ignore all of the things that are different, then that's
 true. If not, well, then no. This is a change. And a good one for the
 reasons listed above.

 Thoughts?







Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Alex Russell
On Tue, Oct 18, 2011 at 6:00 PM, Erik Arvidsson a...@chromium.org wrote:
 On Tue, Oct 18, 2011 at 09:42, Alex Russell slightly...@google.com wrote:
 Ah, but we don't need to care what CSS thinks of our DOM-only API. We
 can live and let live by building on :scope and specifying find* as
 syntactic sugar, defined as:

  HTMLDocument.prototype.find =
  HTMLElement.prototype.find = function(rootedSelector) {
     return this.querySelector(:scope  + rootedSelector);
   }

   HTMLDocument.prototype.findAll =
   HTMLElement.prototype.findAll = function(rootedSelector) {
     return this.querySelectorAll(:scope  + rootedSelector);
   }

 I like the way you think. Can I subscribe to your mailing list?

Heh. Yes ;-)

 One thing to point out with the desugar is that it has a bug and most
 JS libs have the same but. querySelectorAll allows multiple selectors,
 separated by a comma and to do this correctly you need to parse the
 selector which of course requires tons of code so no one does this.
 Lets fix that by building this into the platform.

I agree. I left should have mentioned it. The resolution I think is
most natural is to split on , and assume that all selectors in the
list are :scope prefixed and that. A minor point is how to order the
items in the returned flattened list are ordered (document order? the
natural result of concat()?).



Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Brian Kardell
Some pseudos can contain selector groups, so it would be more than just
split on comma.
On Oct 18, 2011 7:40 PM, Alex Russell slightly...@google.com wrote:

 On Tue, Oct 18, 2011 at 6:00 PM, Erik Arvidsson a...@chromium.org wrote:
  On Tue, Oct 18, 2011 at 09:42, Alex Russell slightly...@google.com
 wrote:
  Ah, but we don't need to care what CSS thinks of our DOM-only API. We
  can live and let live by building on :scope and specifying find* as
  syntactic sugar, defined as:
 
   HTMLDocument.prototype.find =
   HTMLElement.prototype.find = function(rootedSelector) {
  return this.querySelector(:scope  + rootedSelector);
}
 
HTMLDocument.prototype.findAll =
HTMLElement.prototype.findAll = function(rootedSelector) {
  return this.querySelectorAll(:scope  + rootedSelector);
}
 
  I like the way you think. Can I subscribe to your mailing list?

 Heh. Yes ;-)

  One thing to point out with the desugar is that it has a bug and most
  JS libs have the same but. querySelectorAll allows multiple selectors,
  separated by a comma and to do this correctly you need to parse the
  selector which of course requires tons of code so no one does this.
  Lets fix that by building this into the platform.

 I agree. I left should have mentioned it. The resolution I think is
 most natural is to split on , and assume that all selectors in the
 list are :scope prefixed and that. A minor point is how to order the
 items in the returned flattened list are ordered (document order? the
 natural result of concat()?).




Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Sean Hogan

On 19/10/11 7:20 AM, Yehuda Katz wrote:

I agree entirely.

I have asked a number of practitioner friends about this scenario:

div id=parent
p id=childspan id=inlineContent/span/p
/div

  document.getElementById(child).querySelectorAll(div span); // 
returns #inline


In 100% of cases, people consider this behavior *broken*. Not just 
interesting, I wouldn't have expected that, but who came up with 
that!?. In all cases involving JavaScript practitioners, people 
expect querySelectorAll to operate on the element as though the 
element was the root of a new document, and where combinators are 
relative to the element.




It matches the definition of CSS selectors, so I don't think it can be 
called broken. For this case, node.querySelectorAll(div span) finds 
all span's (in document order) which are contained within the invoking 
node and checks that they match the selector expression, in this case 
simply checking they are a descendant of a div.


The new definition being promoted is:
- start at the containing node
- find all descendant div's
- for every div, find all descendant span's.
- with the list of span's, remove duplicates and place in document-order

Once you understand the proper definition it is hard to see this new 
definition as more logical.
To me, the problem here is some (not all) Javascript practitioners not 
learning the proper definition of CSS selectors.


We already knew this was true since all JavaScript libraries that 
implement selectors implemented them in this way.




To me, this indicates that there's no problem here. If you want to use 
an alternative definition of selectors then you use a JS lib that 
supports them. If you want to use the DOM API then you learn how CSS 
selectors work.


I don't see JS libs ever calling the browsers querySelectorAll (or even 
a new findAll) without parsing the selector string first because:

- JS libs support selectors that haven't been implemented on all browsers
- JS libs support selectors that are never going to be part of the standard

Since JS libs will always parse selector strings and call qSA, etc as 
appropriate, I can't see much benefit in creating DOM methods that 
accept non-standard selector strings.


Sean




Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Tab Atkins Jr.
On Tue, Oct 18, 2011 at 4:46 PM, Sean Hogan shogu...@westnet.com.au wrote:
 On 19/10/11 7:20 AM, Yehuda Katz wrote:

 I agree entirely.

 I have asked a number of practitioner friends about this scenario:

 div id=parent
 p id=childspan id=inlineContent/span/p
 /div

  document.getElementById(child).querySelectorAll(div span); // returns
 #inline

 In 100% of cases, people consider this behavior *broken*. Not just
 interesting, I wouldn't have expected that, but who came up with that!?.
 In all cases involving JavaScript practitioners, people expect
 querySelectorAll to operate on the element as though the element was the
 root of a new document, and where combinators are relative to the element.


 It matches the definition of CSS selectors, so I don't think it can be
 called broken. For this case, node.querySelectorAll(div span) finds all
 span's (in document order) which are contained within the invoking node and
 checks that they match the selector expression, in this case simply checking
 they are a descendant of a div.

 The new definition being promoted is:
 - start at the containing node
 - find all descendant div's
 - for every div, find all descendant span's.
 - with the list of span's, remove duplicates and place in document-order

 Once you understand the proper definition it is hard to see this new
 definition as more logical.
 To me, the problem here is some (not all) Javascript practitioners not
 learning the proper definition of CSS selectors.

Not at all.  I'm not sure why you think this is somehow an improper
way to think about things.

There are two ways you can scope a selector.  The first is to filter
the results of a selector match to only those under a certain element
(what QSA does today).  The second is to scope the entire selector to
only apply underneath the scoping element, which is what Alex is
proposing.

An alternative view of this is that current QSA restricts the final
compound selector in a selector to match only elements in the scope,
while allowing the rest of the selector to match elements anywhere in
the document.  Alex's proposal (and every other JS selector engine)
restricts all of the selector component to matching only elements in
the scope.

There is nothing unnatural or improper about this.  The fact that
every JS selector engine works in the latter fashion, and that JS devs
are regularly surprised by the former behavior, suggests strongly that
the latter behavior is the better default behavior.

Based on discussion on the mailing list, style scoped will be
changing to the latter behavior as well, with the ability to invoke
the former behavior in the rare circumstances when you explicitly want
it.

~TJ



Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Alex Russell
Hi Matt,

On Tue, Oct 18, 2011 at 6:25 PM, Matt Shulman mat...@google.com wrote:
 I think the query selector functionality is important enough that one
 could easily justify adding additional APIs to make this work
 better/faster, even if they overlap with existing APIs.  But, it would
 be unfortunate if more APIs were added to the DOM and libraries still
 weren't able to use them because the semantics didn't end up being
 quite right.
 It seems like the right approach would be to take jquery and rewrite
 it to use this new API and then see empirically whether it gives the
 same selection behavior as before and see how much of a performance or
 simplicity gain there is after doing this.

No need to wait. We had something nearly identical for this in Dojo
using an ID prefix hack. It looked something like this:

(function(){
var ctr = 0;
query = function(query, root){
root = root||document;
var rootIsDoc = (root.nodeType == 9);
var doc = rootIsDoc ? root : 
(root.ownerDocment||document);

if(!rootIsDoc || (~+.indexOf(query.charAt(0)) = 0)){
// Generate an ID prefix for the selector
root.id = root.id||(qUnique+(ctr++));
query = #+root.id+ +query;
}

return Array.prototype.slice.call(
doc.querySelectorAll(query)
);
};
})();

This is exactly the same dance that :scope does.

 (I think it's a good thing to allow selectors to start with
 combinators.  That seems very useful.)

 On Tue, Oct 18, 2011 at 9:47 AM, Alex Russell slightly...@google.com wrote:
 On Tue, Oct 18, 2011 at 5:42 PM, Alex Russell slightly...@google.com wrote:
 Lachlan and I have been having an...um...*spirited* twitter discussion
 regarding querySelectorAll, the (deceased?) queryScopedSelectorAll,
 and :scope. He asked me to continue here, so I'll try to keep it
 short:

 The rooted forms of querySelector and querySelectorAll are mis-designed.

 Discussions about a Scoped variant or :scope pseudo tacitly
 acknowledge this, and the JS libraries are proof in their own right:
 no major JS library exposes the QSA semantic, instead choosing to
 implement a rooted search.

 Related and equally important, that querySelector and querySelectorAll
 are often referred to by the abbreviation QSA suggests that its name
 is bloated and improved versions should have shorter names. APIs gain
 use both through naming and through use.

 Sorry, this should say meaning. APIs gain *meaning* through both use
 and naming.

 On today's internet -- the
 one where 50% of all websites include jQuery -- you could even go with
 element.$(selector) and everyone would know what you mean: it's
 clearly a search rooted at the element on the left-hand side of the
 dot.

 Ceteris peribus, shorter is better. When there's a tie that needs to
 be broken, the more frequently used the API, the shorter the name it
 deserves -- i.e., the larger the component of its meaning it will gain
 through use and repetition and not naming and documentation.

 I know some on this list might disagree, but all of the above is
 incredibly non-controversial today. Even if there may have been
 debates about scoping or naming when QSA was originally designed,
 history has settled them. And QSA lost on both counts.

 I therefore believe that this group's current design for scoped
 selection could be improved significantly. If I understand the latest
 draft (http://www.w3.org/TR/selectors-api2/#the-scope-pseudo-class)
 correctly, a scoped search for multiple elements would be written as:

   element.querySelectorAll(:scope  div  .thinger);

 Both then name and the need to specify :scope are punitive to
 readers and writers of this code. The selector is *obviously*
 happening in relationship to element somehow. The only sane
 relationship (from a modern JS hacker's perspective) is that it's
 where our selector starts from. I'd like to instead propose that we
 shorten all of this up and kill both stones by introducing a new API
 pair, find and findAll, that are rooted as JS devs expect. The
 above becomes:

   element.findAll( div  .thinger);

 Out come the knives! You can't start a selector with a combinator!

 Ah, but we don't need to care what CSS thinks of our DOM-only API. We
 can live and let live by building on :scope and specifying find* as
 syntactic sugar, defined as:

  HTMLDocument.prototype.find =
  HTMLElement.prototype.find = function(rootedSelector) {
     return this.querySelector(:scope  + rootedSelector);
   }

   HTMLDocument.prototype.findAll =
   HTMLElement.prototype.findAll = function(rootedSelector) {
     return this.querySelectorAll(:scope  + rootedSelector);
   }

 Of course, :scope in this case is just a special case of the 

Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Alex Russell
On Tue, Oct 18, 2011 at 8:59 PM, Brian Kardell bkard...@gmail.com wrote:
 I know that there were discussions that crossed over into CSS about a
 @global or a :context which could sort of include things outside the
 scope as part of the query but not be the subject.  Does any of that
 relate here?

I suppose it does, but only as an implementation detail. Nothing more
than the ID prefix hack or :scope are really necessary to get the
API we need.

 PS
 Out come the knives! You can't start a selector with a combinator!
 Even on CSS lists this has been proposed inside of pseudos... Numerous
 times and in numerous contexts.   It seems to me that everyone (even
 the people who disagree with the proposal) knows what it means
 immediately - but you are right... That's always the response.  So at
 the risk of being stabbed by an angry mob:  Can someone explain _why_
 you can't - under absolutely any circumstances - begin a selector with
 a combinator - even if there appears to be wide agreement that it
 makes sense in a finite set of circumstances?



 On Tue, Oct 18, 2011 at 12:42 PM, Alex Russell slightly...@google.com wrote:
 Lachlan and I have been having an...um...*spirited* twitter discussion
 regarding querySelectorAll, the (deceased?) queryScopedSelectorAll,
 and :scope. He asked me to continue here, so I'll try to keep it
 short:

 The rooted forms of querySelector and querySelectorAll are mis-designed.

 Discussions about a Scoped variant or :scope pseudo tacitly
 acknowledge this, and the JS libraries are proof in their own right:
 no major JS library exposes the QSA semantic, instead choosing to
 implement a rooted search.

 Related and equally important, that querySelector and querySelectorAll
 are often referred to by the abbreviation QSA suggests that its name
 is bloated and improved versions should have shorter names. APIs gain
 use both through naming and through use. On today's internet -- the
 one where 50% of all websites include jQuery -- you could even go with
 element.$(selector) and everyone would know what you mean: it's
 clearly a search rooted at the element on the left-hand side of the
 dot.

 Ceteris peribus, shorter is better. When there's a tie that needs to
 be broken, the more frequently used the API, the shorter the name it
 deserves -- i.e., the larger the component of its meaning it will gain
 through use and repetition and not naming and documentation.

 I know some on this list might disagree, but all of the above is
 incredibly non-controversial today. Even if there may have been
 debates about scoping or naming when QSA was originally designed,
 history has settled them. And QSA lost on both counts.

 I therefore believe that this group's current design for scoped
 selection could be improved significantly. If I understand the latest
 draft (http://www.w3.org/TR/selectors-api2/#the-scope-pseudo-class)
 correctly, a scoped search for multiple elements would be written as:

   element.querySelectorAll(:scope  div  .thinger);

 Both then name and the need to specify :scope are punitive to
 readers and writers of this code. The selector is *obviously*
 happening in relationship to element somehow. The only sane
 relationship (from a modern JS hacker's perspective) is that it's
 where our selector starts from. I'd like to instead propose that we
 shorten all of this up and kill both stones by introducing a new API
 pair, find and findAll, that are rooted as JS devs expect. The
 above becomes:

   element.findAll( div  .thinger);

 Out come the knives! You can't start a selector with a combinator!

 Ah, but we don't need to care what CSS thinks of our DOM-only API. We
 can live and let live by building on :scope and specifying find* as
 syntactic sugar, defined as:

  HTMLDocument.prototype.find =
  HTMLElement.prototype.find = function(rootedSelector) {
     return this.querySelector(:scope  + rootedSelector);
   }

   HTMLDocument.prototype.findAll =
   HTMLElement.prototype.findAll = function(rootedSelector) {
     return this.querySelectorAll(:scope  + rootedSelector);
   }

 Of course, :scope in this case is just a special case of the ID
 rooting hack, but if we're going to have it, we can kill both birds
 with it.

 Obvious follow up questions:

 Q.) Why do we need this at all? Don't the toolkits already just do
 this internally?
 A.) Are you saying everyone, everywhere, all the time should need to
 use a toolkit to get sane behavior from the DOM? If so, what are we
 doing here, exactly?

 Q.) Shorter names? Those are for weaklings!
 A.) And humans. Who still constitute most of our developers. Won't
 someone please think of the humans?

 Q.) You're just duplicating things!
 A.) If you ignore all of the things that are different, then that's
 true. If not, well, then no. This is a change. And a good one for the
 reasons listed above.

 Thoughts?






Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Alex Russell
On Tue, Oct 18, 2011 at 9:40 PM, Boris Zbarsky bzbar...@mit.edu wrote:
 On 10/18/11 4:20 PM, Yehuda Katz wrote:

  * Speeding up certain operations like `#foo` and `body`. There is *no
    excuse* for it being possible to implement userland hacks that
    improve on the performance of querySelectorAll.

 Sure there is.  One such excuse, for example, is that the userland hacks
 have different behavior from querySelectorAll in many cases.  Now the author
 happens to know that the difference doesn't matter in their case, but the
 _browser_ has no way to know that.

 The other excuse is that adding special cases (which is what you're asking
 for) slows down all the non-special-case codepaths.  That may be fine for
 _your_ usage of querySelectorAll, where you use it with a particular limited
 set of selectors, but it's not obvious that this is always a win.

Most browsers try to optimize what is common. Or has that fallen out
of favor while I wasn't looking?

 This may be the result of browsers failing to cache the result of parsing
 selectors

 Yep.  Browsers don't cache it.  There's generally no reason to.  I have yet
 to see any real-life testcase bottlenecked on this part of querySelectorAll
 performance.

    or something else, but the fact remains that qSA can be noticably
    slower than the old DOM methods, even when Sizzle needs to parse the
    selector to look for fast-paths.

 I'd love to see testcases showing this.

 jQuery also handles certain custom pseudoselectors, and it might be nice
 if it was possible to register JavaScript functions that qSA would use
 if it found an unknown pseudo

 This is _very_ hard to reasonably unless the browser can trust those
 functions to not do anything weird.  Which of course it can't.  So your
 options are either much slower selector matching or not having this. Your
 pick.

 -Boris





Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Alex Russell
On Wed, Oct 19, 2011 at 12:45 AM, Brian Kardell bkard...@gmail.com wrote:
 Some pseudos can contain selector groups, so it would be more than just
 split on comma.

Yes, yes, of course. I've written one of these parsers. Just saying
that the impl would split selector groups and prefix them all with
:scope 

 On Oct 18, 2011 7:40 PM, Alex Russell slightly...@google.com wrote:

 On Tue, Oct 18, 2011 at 6:00 PM, Erik Arvidsson a...@chromium.org wrote:
  On Tue, Oct 18, 2011 at 09:42, Alex Russell slightly...@google.com
  wrote:
  Ah, but we don't need to care what CSS thinks of our DOM-only API. We
  can live and let live by building on :scope and specifying find* as
  syntactic sugar, defined as:
 
   HTMLDocument.prototype.find =
   HTMLElement.prototype.find = function(rootedSelector) {
      return this.querySelector(:scope  + rootedSelector);
    }
 
    HTMLDocument.prototype.findAll =
    HTMLElement.prototype.findAll = function(rootedSelector) {
      return this.querySelectorAll(:scope  + rootedSelector);
    }
 
  I like the way you think. Can I subscribe to your mailing list?

 Heh. Yes ;-)

  One thing to point out with the desugar is that it has a bug and most
  JS libs have the same but. querySelectorAll allows multiple selectors,
  separated by a comma and to do this correctly you need to parse the
  selector which of course requires tons of code so no one does this.
  Lets fix that by building this into the platform.

 I agree. I left should have mentioned it. The resolution I think is
 most natural is to split on , and assume that all selectors in the
 list are :scope prefixed and that. A minor point is how to order the
 items in the returned flattened list are ordered (document order? the
 natural result of concat()?).





Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Alex Russell
On Wed, Oct 19, 2011 at 12:46 AM, Sean Hogan shogu...@westnet.com.au wrote:
 On 19/10/11 7:20 AM, Yehuda Katz wrote:

 I agree entirely.

 I have asked a number of practitioner friends about this scenario:

 div id=parent
 p id=childspan id=inlineContent/span/p
 /div

  document.getElementById(child).querySelectorAll(div span); // returns
 #inline

 In 100% of cases, people consider this behavior *broken*. Not just
 interesting, I wouldn't have expected that, but who came up with that!?.
 In all cases involving JavaScript practitioners, people expect
 querySelectorAll to operate on the element as though the element was the
 root of a new document, and where combinators are relative to the element.


 It matches the definition of CSS selectors, so I don't think it can be
 called broken. For this case, node.querySelectorAll(div span) finds all
 span's (in document order) which are contained within the invoking node and
 checks that they match the selector expression, in this case simply checking
 they are a descendant of a div.

 The new definition being promoted is:
 - start at the containing node
 - find all descendant div's
 - for every div, find all descendant span's.
 - with the list of span's, remove duplicates and place in document-order

 Once you understand the proper definition it is hard to see this new
 definition as more logical.
 To me, the problem here is some (not all) Javascript practitioners not
 learning the proper definition of CSS selectors.

I'm just going to assume you're trolling and not respond to anything
else you post here.

 We already knew this was true since all JavaScript libraries that
 implement selectors implemented them in this way.


 To me, this indicates that there's no problem here. If you want to use an
 alternative definition of selectors then you use a JS lib that supports
 them. If you want to use the DOM API then you learn how CSS selectors work.

 I don't see JS libs ever calling the browsers querySelectorAll (or even a
 new findAll) without parsing the selector string first because:
 - JS libs support selectors that haven't been implemented on all browsers
 - JS libs support selectors that are never going to be part of the standard

 Since JS libs will always parse selector strings and call qSA, etc as
 appropriate, I can't see much benefit in creating DOM methods that accept
 non-standard selector strings.

 Sean





Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Sean Hogan

On 19/10/11 10:58 AM, Tab Atkins Jr. wrote:

On Tue, Oct 18, 2011 at 4:46 PM, Sean Hoganshogu...@westnet.com.au  wrote:

On 19/10/11 7:20 AM, Yehuda Katz wrote:

I agree entirely.

I have asked a number of practitioner friends about this scenario:

div id=parent
p id=childspan id=inlineContent/span/p
/div

  document.getElementById(child).querySelectorAll(div span); // returns
#inline

In 100% of cases, people consider this behavior *broken*. Not just
interesting, I wouldn't have expected that, but who came up with that!?.
In all cases involving JavaScript practitioners, people expect
querySelectorAll to operate on the element as though the element was the
root of a new document, and where combinators are relative to the element.


It matches the definition of CSS selectors, so I don't think it can be
called broken. For this case, node.querySelectorAll(div span) finds all
span's (in document order) which are contained within the invoking node and
checks that they match the selector expression, in this case simply checking
they are a descendant of a div.

The new definition being promoted is:
- start at the containing node
- find all descendant div's
- for every div, find all descendant span's.
- with the list of span's, remove duplicates and place in document-order

Once you understand the proper definition it is hard to see this new
definition as more logical.
To me, the problem here is some (not all) Javascript practitioners not
learning the proper definition of CSS selectors.

Not at all.  I'm not sure why you think this is somehow an improper
way to think about things.

There are two ways you can scope a selector.  The first is to filter
the results of a selector match to only those under a certain element
(what QSA does today).  The second is to scope the entire selector to
only apply underneath the scoping element, which is what Alex is
proposing.

An alternative view of this is that current QSA restricts the final
compound selector in a selector to match only elements in the scope,
while allowing the rest of the selector to match elements anywhere in
the document.  Alex's proposal (and every other JS selector engine)
restricts all of the selector component to matching only elements in
the scope.

There is nothing unnatural or improper about this.  The fact that
every JS selector engine works in the latter fashion, and that JS devs
are regularly surprised by the former behavior, suggests strongly that
the latter behavior is the better default behavior.

Based on discussion on the mailing list,style scoped  will be
changing to the latter behavior as well, with the ability to invoke
the former behavior in the rare circumstances when you explicitly want
it.


If it becomes part of the standard definition of CSS selectors then it 
can be supported by a query method. At that point the only discussion is 
for the name.


However, in reading that thread I don't see any mention of selectors 
such as  div  span. Did I miss something?


Sean




Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Boris Zbarsky

On 10/18/11 6:05 PM, Brian Kardell wrote:

They would run in their own sandbox and they would have access to the
parameters passed into the function by way of pattern.


OK; I think that people might have a pretty tough time with a 
programming environment like that... but maybe.



The 'match' in this case wouldn't be
a mutable DOM element.  You can give it a smaller API by saying that
the 'lite' version of the element that is passed in has no properties
which might give you something mutable


So no properties at all?


- or you can say that all
methods/properties would also return immutable shadows of themselves.


It'd have to be that...


I would be happy to walk through more detailed ideas in terms of what
specifically that would look like if there were some kind of initial
yeah, that might work - its worth looking into some more :)


On my part it's a yeah, it might work, with a huge amount of effort, 
probably disproportionate to the utility.  At least at first blush.


-Boris



Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Boris Zbarsky

On 10/18/11 7:38 PM, Alex Russell wrote:

The resolution I think is most natural is to split on ,


That fails with :any, with the expanded :not syntax, on attr selectors, etc.

You can split on ',' while observing proper paren and quote nesting, but 
that can get pretty complicated.



A minor point is how to order the
items in the returned flattened list are ordered (document order? the
natural result of concat()?).


Document order.

-Boris




Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Boris Zbarsky

On 10/18/11 8:08 PM, Alex Russell wrote:

The other excuse is that adding special cases (which is what you're asking
for) slows down all the non-special-case codepaths.  That may be fine for
_your_ usage of querySelectorAll, where you use it with a particular limited
set of selectors, but it's not obvious that this is always a win.


Most browsers try to optimize what is common.


Yes, but what is common for Yehuda may well not be globally common.

There's also the question of premature optimization.  Again, I'd love to 
see a non-synthetic situation where any of this matters.  That would be 
a much more useful point to reason from than some sort of hypothetical 
faith-based optimization.


-Boris



Re: QSA, the problem with :scope, and naming

2011-10-18 Thread Ojan Vafai
Overall, I wholeheartedly support the proposal.

I don't really see the benefit of allowing starting with a combinator. I
think it's a rare case that you actually care about the scope element and in
those cases, using :scope is fine. Instead of element.findAll( div 
.thinger), you use element.findAll(:scope  div  .thinger). That said, I
don't object to considering the :scope implied if the selector starts with a
combinator.

On Tue, Oct 18, 2011 at 6:15 PM, Boris Zbarsky bzbar...@mit.edu wrote:

 On 10/18/11 7:38 PM, Alex Russell wrote:

 The resolution I think is most natural is to split on ,


 That fails with :any, with the expanded :not syntax, on attr selectors,
 etc.

 You can split on ',' while observing proper paren and quote nesting, but
 that can get pretty complicated.


Can we define it as a sequence of selectors and be done with it? That way it
can be defined as using the same parsing as CSS.


 A minor point is how to order the
 items in the returned flattened list are ordered (document order? the
 natural result of concat()?).


 Document order.


Definitely.


 -Boris





<    1   2