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

Hi Matt,

On Tue, Oct 18, 2011 at 6:25 PM, Matt Shulman <mattsh@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 <slightlyoff@google.com> wrote:
>> On Tue, Oct 18, 2011 at 5:42 PM, Alex Russell <slightlyoff@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?
>>>
>>
>>
>

Received on Wednesday, 19 October 2011 00:01:38 UTC