One of the many features that I like about jQuery is the ability to pass in CSS selectors and HTML attributes into the jQuery
for retrieving DOM nodes. YUI has methods for fetching elements by their className
, but not by their HTML attributes, such as type
or name
. Today, well cover a method I wrote that supports fetching elements by tagName, className, and/or a collection of attributes. The method
getElements returns all nodes that match any of the tagNames and any of the classNames and every type of attribute (you can have multiple attribute types, such as
name and
type) provided:
Example 1: getElements
Dom.getElements = function(tagNames, classNames, attributes, root) { var elems = [], nodes = []; // does the root exist, then use it if (root) { root = Dom.get(root); // if no root node, then no children; return empty array if (! root) {return [];} } // otherwise use document else { root = $doc; } // there are tag names provided if (tagNames && tagNames.length) { tagNames.batch(function(tag) { elems = elems.concat(Array.get(root.getElementsByTagName(tag))); }); } // use wildcard *for tag name else { elems = Array.get(root.getElementsByTagName(*)).slice(1); if (! elems.length && root.all) {elems = root.all;} // checking for IE < 6 } if (classNames || (attributes && attributes.length)) { var classFn = function(node) {return node;}, attrFn = function(node) {return node;}; // update the classFn to look at a list of classes if (classNames && classNames.length) { classFn = function(node) { return classNames.batch(function(klass) { if (Dom.hasClass(node, klass)) { return node; } }); }; } // update the attrFn to look at a list of attributes if (attributes && attributes.length) { var attributeMap = {}, attributeTypes = []; // iterate on the attributes and create iterables attributes.batch(function(attr) { if (! attributeMap[attr.attributeName]) { attributeMap[attr.attributeName] = []; attributeTypes.push(attr.attributeName); } attributeMap[attr.attributeName].push(attr.attributeValue); }); attrFn = function(node) { var isMatch = true; // iterate on the attribute types to find keys for the attribute value map attributeTypes.batch(function(attributeName) { // iterate on the values and compare with node var n = attributeMap[attributeName].batch(function(attributeValue) { if (attributeValue === node[attributeName]) {return node;} }); // node did not match, stop iterating, since this is not a match if (! n) { isMatch = false; return true; } }); return isMatch ? node : null; }; } // iterate on the nodes found by tags elems.batch(function(el) { if (classFn(el) && attrFn(el)) {nodes.push(el);} }); return nodes; } else { return elems; } };
First, I have shortened "YAHOO.util.Dom" to Dom and this function required my Array.js to convert DOM node collections into arrays. The parameters for
getElemetns are four optional values: an array of tag name strings, an array or class name strings, an array of attribute objects {attributeName, attributeValue}, and the root node to search the DOM from. If the root node is not provided, then
getElements will start searching from the document, but if an invalid root node is provided, then
getElements will return an empty array. If tag names are provided, then
getElements concatenates the results of each into an array (
elems), but will otherwise default to wildcard (
*), retrieving all sub-nodes.
When class names or attributes are provided, getElements will iterate on the
elems array and create a new array
nodes containing the elements that matched the class and attribute values. However, if neither is provided, we simply return the
elems array. To determine if the classes and/or attributes are present on a node, we create functions that take a node as a parameter and return that node when the value matches. The class name function will test to see if the node contains any of the classes in the array. The attribute function first creates a map of values for each type of attribute that is provided, then iterates through them, only returning true when the node matches at least 1 attribute of each type.
Here is an example that starts searching for input and
a tags, with either class
button or
link, and has the name
submit:
Example 2: Using getElements
Dom.getElements([input,a], [button,link], [{attributeName:name, attributeValue:submit}],myRootNode);