Prototype's latest development code featured the awesome $$() that can select elements by passing it a CSS selector.. Though this is still on the development respository and not released yet, I know many people who uses it, including me.
I've added a tiny functionality to the $$() that makes it even more useful and usable IMO. It allows specifying any node that implements getElementsByTagName (that means any XMLDocument node, and most of other HTML nodes) to be selected as the root of the search. Simply by appending this node object to the list of selectors you pass to $$(). Also, it doesn't force you to do so, if you don't need this functionality, simply don't pass anything and it will work --as it does by default-- on the document object.
Need I say that it does only work on the currect development version of prototype ?
Installation
To add this feature, simply load the following code after loading prototype.js:
Selector.prototype.findElements = function(scope, root) {
if (root == undefined) {
root = document;
}
var element;
if (element = $(this.params.id))
if (this.match(element))
if (!scope || Element.childOf(element, scope))
return [element];
scope = (scope || root).getElementsByTagName(this.params.tagName || '*');
var results = [];
for (var i = 0; i < scope.length; i++)
if (this.match(element = scope[i]))
results.push(Element.extend(element));
return results;
};
function $$() {
var scope = document;
arguments = $A(arguments);
if (typeof arguments.last() == 'object') {
scope = arguments.pop();
}
return arguments.map(function(expression) {
return expression.strip().split(/\s+/).inject([null], function(results, expr) {
var selector = new Selector(expr);
return results.map(function (value, index) {
return selector.findElements.bind(selector)(value, scope);
}).flatten();
});
}).flatten();
}Usage Examples:
Selecting elements from an AJAX's responseXML
Here we use $$() to select elements from another DOM tree we got from AJAX object..
<?php
new Ajax.Request('http://example.org/list.php', {
onSuccess : function (r) {
var newNodes = $$('div.node', r.responseXML);
},
method : 'get'
});
?>Selecting an element from an iteration
Another example, is when you are iterating over nodes that has the same selector, and need to select some of each node's childs without going out of the iteration context. An example will clarify this more..
Here I was working with a simple DOM that looked like this:<div class="item">
<h2 class="title"><a href="/item/1">My first item</a></h2>
<div class="content">
<p>item content goes here....</p>
</div>
</div>
<div class="item">
<h2 class="title"><a href="/item/2">My second item</a></h2>
<div class="content">
<p>item content goes here....</p>
</div>
</div>
<div class="item">
<h2 class="title"><a href="/item/3">My third item</a></h2>
<div class="content">
<p>item content goes here....</p>
</div>
</div>I needed to iterate over each item to do the following:
- insert new child node at the bottom of <div class="content">.
- add an event to the <a>.
<?php
$$('div.item').each( function (item) {
/* insert new child node */
var s = document.createElement('span');
..........
...
$$('div.content', item).first().appendChild(s);
/* get the <a> node */
var linkNode = $$('h2 a', item).first();
addEvent(linkNode, 'click', function() {...});
});
?>Notice that in $$('div.content', item).first().appendChild(s);, If I wasn't able to specify the root of the search (item here), no CSS selector would have allowed me to select the div.content for the particular node I'm processing in each iteration. Same for var linkNode = $$('h2 a', item).first();
Last words
I didn't use prototype that much yet, so if you know a way that could give similiar results without having to apply my code, please let me know.