Re: QSA, the problem with :scope, and naming
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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