Re: Opera's Proposal for :context Selector

Lachlan Hunt wrote:
> Andrew Fedoniouk wrote:
>> Lachlan Hunt wrote:
>>> i.e. Consider the following fragment:
>>>
>>> <body>
>>>   <section id="foo">
>>>     <div>
>>>       ...
>>>     </div>
>>>   </section>
>>> </body>
>>>
>>> foo.querySelector("body div");
>>>
>>> As currently defined and as it is being implemented by browsers, 
>>> that would match and return the above div element.  This is because 
>>> the Selectors spec defines whether or not a given element matches a 
>>> particular element, and the API does not redefine that.
>>
>> If there are such implementations that have
>> querySelector(); implemented for arbitrary DOM elements then
>> without :context/:root they are barely useful.
>
> No implementations have shipped yet, but the API is implemented in 
> recent betas, nightlies or experimental builds of the browsers.
>
>>> If :root were redefined as you suggest, then so would this would 
>>> need to as well:
>>>
>>> foo.querySelector("body :root div");
>>
>> No. That is invalid selector. :root can only be defined for the very 
>> first element of the selector. Otherwise it makes no sense - will 
>> always be false.
>
> Yes, I know :root can only be the first element, which is why I'm 
> saying it can't be used in this case!

Why? The idea of :root to be a pseudo-class imply that different
elements may have this state/role assigned.

Otherwise why not to use element html as it is?
Write something like this and it will be perfectly fine:

html > body > div { ... }

>
>>> (Note that will in fact work with the :context selector in place of 
>>> :root.)
>>
>> foo.querySelector("body :context div");
>>
>> That is very ineffective selector.
>
> In that particular case, yes, it's entirely redundant.  But consider 
> this case:
>
> foo.querySelectorAll(".foo :context>.xxx, .bar :context>.yyy");
>
> It allows you to select different child elements based on the parent 
> or other ancestor element.

(That one even worse to be honest. Lookups by solely class names
are more expensive than by tag name. But that are nuances, indeed)

>
>> Say you have N children of the element. Then for all N children
>> you will execute the same testing sequence "body :context".
>>
>> In fact such things should be just disallowed.
>>
>> If you really need such a selector then you should write it as:
>>
>> var div = self.selectParent("body")? self.selectChild("div"):null;
>>
>> this will be the most effective way of doing
>>   foo.querySelector("body :context div");
>
> Why are 2 function calls more efficient than a single function call 
> that is already compatible with the spec and implementations (pending 
> support for :context)?

Selector "body :context div" in context of selector queries can be
deciphered this way:

1) If the element (context) is inside body then
2) return first div found among its children.

You need to run #1 only once. And if *only* it is true
then run second sup-part test against all children.

So if :root will be declared as a lookup root
for the .querySelector()/.selectChild() then
it means that vendors will be forced to implement
the most optimal lookup method and users will be
shielded from using it ineffectively.

>
>> I've already implemented style-set's [1] that are such scoped style 
>> sheets (but defined purely in CSS). We have more than a year of real 
>> experience of using scoped style systems.
>
> It appears that your style sets were designed to meet different use 
> cases from scoped stylesheets in HTML5.

Both approached are designed for better componentization/modularization.

Think about <input type="calendar" /> like here:
http://www.terrainformatica.com/htmlayout/images/datetime.jpg
In our case contents of such input element is an ordinary DOM element
with sub-tree seen to CSS processor. Users are willing to style things
like td.today or .current-month inside it.

Such input element uses scoped style system that is isolated
from the rest and due to shorter lookups it is pretty effective.

And yet you can use that style-sets in the meaning of <html5>.

>> jQuery allows you to write:
>>
>> $(this).parent("ul.collapsible")
>>
>> See: http://docs.jquery.com/Traversing/parent#expr
>
> That page doesn't offer any examples or use cases for why the 
> expression is useful.  

Appears that the url is getting resolved for you to something
different at all.

This example is from the page above:

$("p").parent(".selected").css("background", "yellow");


> But at least that is some evidence to support your request
> and could be something worth considering for version 2 of the spec.  
> But it is orthogonal to the :context selector issue being discussed 
> here, and so I won't discuss it further in this thread.

It is not orthogonal. element.selectParent() by definition shall
treat :root/:context as a global document root.

>
>> Moreover: minimal set of selector operations that is needed
>> for good native support of jQuery contains these three functions:
>>
>> element.selectChild(selector[, callback]);
>> element.selectParent(selector[, callback]);
>> element.matches(selector);
>
> The matches() function has been considered before and could possibly 
> be introduced in version 2, but the use cases need to be investigated 
> first.

In principle selectParent can be implemented this way:

function selectParent(selector)
{
  var p = this.parent();
  while( p )
  {
    if( p.matches(selector) )
      return true;
    p = this.parent();
  }
}

but that requires parsing of the same statement inside the loop.
Not good.

>
>> Too bad. That is one of reasons why JS based solutions are slow. Slow 
>> by design.
>>
>> Write function that contains something like this
>>   elem.querySelector("*").length
>> and you can name it as JoyOfGarbageCollector().
>
> The API was designed to allow scripts to obtain a collection of 
> elements that they actually want to do something with.  I agree, it's 
> not optimal if you only wish to count the number of matching elements 
> and then discard the list, but then I'm not sure why such information 
> would be useful on its own.  The typical case for getting the length 
> of a list is for iteration, in which case the elements themselves are 
> usually used within the loop anyway.  But again, this is orthogonal to 
> the issue at hand.
>

You never know what type of collection for the elements authors will 
need. It can be array or keyed map or something else.
In 99% of cases that list returned by querySelectorAll will be used 
locally inside the function for just enumeration - transformation it
to something different. But in the way it was proposed it has severe 
side effect - it always pollute the heap. That is bad design of public 
API in my opinion.

Function that returns list can be written by the author simply as:

function $$(selector, root = document.rootElement())
{
  var result = [];
  this.selectChild(selector, function(el) { result.push(el) } );
  return result;
}


-- 
Andrew Fedoniouk.

http://terrainformatica.com

Received on Friday, 11 July 2008 18:41:04 UTC