This project is archived and is in readonly mode.

#685 ✓resolved
Jesse

Rooted Selectors Support, Like $$(':root > tag.class')

Reported by Jesse | June 12th, 2009 @ 07:56 AM | in 1.3.0 rc2 (closed)

I would love Selectors to support an implicit or explicit root element. So that if you have something like:

var a = $(el).getElements('> a');       /* implicit root */
var b = $(el).getElements(':root > a'); /* explicit root */
var c = $(el).getElements('$ > a');     /* explicit root, using '$' char */

it means "direct decedent of the calling element - $(el)". This is beyond useful, doesn't seem, off hand, to disturb any existing Selector use, and really doesn't have a simple equivalent in MooTools - there is no consistent, easy and fast other easy way to achieve the same thing.

Suppose the HTML case below:

<body>
   <div id="wdgt-1" class="widget">
      <h2 class="title">...</h2>
      <div class="content">
         ...
            <div id="wdgt-3" class="widget">
               <h2 class="title">...</h2>
               <div class="content">
                  ...
               </div>
            </div>
         ...
      </div>
   </div>
 
   <div id="wdgt-2" class="widget">
      <h2 class="title">...</h2>
      <div class="content">
         ...
      </div>
   </div>
</body>

In your JavaScript you have an onClick event on all the 'div.widget' elements, it would be great to be able to do this:

var click = function(event) {
   var title = $(event.target).getElement('> h2.title');
};

In this way we get only the 'h2.title' we want, and not any 'h2.title' elements from another widget buried in the 'div.content' of this widget.

Clearly, in this case, there are simple methods of getting just the 'h2.title' I want - but imagine that the CSS Selector I want is more complex, '> h2.title span.btns', other DOM traversing methods start to get cumbersome.

Also, even though $(event.target).getElement('h2.title') will return what I want here:
1. MooTools, the way it is implemented, would first find ALL matching elements and then return just to first. This is inefficient in the case where there are 100's of matching children. Especially if the CSS selector was more complicated so that more logic is wasted on elements that will never be returned.
2. Still doesn't help in the cases of other CSS selectors such as '+ tag.element' or '~ tag.element'.

To summarize, it'd be very useful if MooTools supported an implicit (or explicit) root element in the Selector syntax such that if '>', '~', or '+' appear as the first character of the selector then the root element is implicitly matched.

It turns out that the code change to MooTools is minor. Here is is for the search() method in 1.2.2 (comment shows change):

Selectors.Utils.search = function(self, expression, local) {
    var splitters = [];
 
    var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1, m2){
        splitters.push(m1);
        return ':)' + m2;
    }).split(':)');
    if(splitters.length) console.log(JSON.encode(selectors)+"\n"+JSON.encode(splitters));
 
    var items, filtered, item;
 
    for (var i = 0, l = selectors.length; i < l; i++){
 
        var selector = selectors[i];
 
        if (i == 0)
        {
            /* This minor addition allows implicit root, '> tag.class' */
            if (selector == ''){ /* or '$' for '$' as explicit root */
                items = [self];
                continue;
            }
            if (Selectors.RegExps.quick.test(selector)){
                items = self.getElementsByTagName(selector);
                continue;
            }
        }
 
        var splitter = splitters[i - 1];
 
        var tagid = Selectors.Utils.parseTagAndID(selector);
        var tag = tagid[0], id = tagid[1];
 
        if (i == 0){
            items = Selectors.Utils.getByTagAndID(self, tag, id);
        } else {
            var uniques = {}, found = [];
            for (var j = 0, k = items.length; j < k; j++) found = Selectors.Getters[splitter](found, items[j], tag, id, uniques);
            items = found;
        }
 
        var parsed = Selectors.Utils.parseSelector(selector);
 
        if (parsed){
            filtered = [];
            for (var m = 0, n = items.length; m < n; m++){
                item = items[m];
                if (Selectors.Utils.filter(item, parsed, local)) filtered.push(item);
            }
            items = filtered;
        }
 
    }
 
    return items;
 
};

Please consider.

I could make the case better here but am hoping you can already see the benefit.

My other post highlighting use: http://www.mooforum.net/general12/tag-class-support-t1796.html#p5873

John Resig mention of same idea: http://ejohn.org/blog/thoughts-on-queryselectorall/
Section: "Combinator-rooted Queries"

Thanks,
Jesse

Comments and changes to this ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

Shared Ticket Bins

Pages