Internet Pirate Guild

2008-03-26

The Ultimate getElementsByClassName

The Ultimate getElementsByClassName

Revised version May 11th 2007



function getElementsByClassName(className, tag, elm){
var testClass = new RegExp("(^|\\\\s)" + className + "(\\\\s|$)");
var tag = tag || "*";
var elm = elm || document;
var elements = (tag == "*" && elm.all)? elm.all : elm.getElementsByTagName(tag);
var returnElements = [];
var current;
var length = elements.length;
for(var i=0; i current = elements[i];
if(testClass.test(current.className)){
returnElements.push(current);
}
}
return returnElements;
}

Good JavaScript usage on the Internet is based on making it unobtrusive,
meaning that web pages aren’t dependant on it to work and that that the title="HyperText Markup Language">HTML code shouldn’t be riddled with
inline event handlers and JavaScripts. Using javascript: is
forbidden, stop that!


What you do is to apply the events to desired elements from an external
JavaScript file, normally performed when the page has loaded. For instance, if
you want to apply a certain event to some a elements, you loop
through the a elements in the page and then apply the events
accordingly, e.g. if the element has a certain class name.


Last week, I felt the need to have a script that accessed all elements in a
web page with a certain class name and returned them as an array to work with. I
wrote my function, but ran into problems when it came to distinguishing class
names that contained a hyphen (-). Then I remembered that href="http://www.snook.ca/jonathan/">Jonathan Snook href="http://www.snook.ca/archives/000370.php">wrote a function a while ago,
so I went to his web page to see if I’d missed something. Interestingly enough,
it was very similar to mine, and when I tested his it didn’t work either.


So, since Jonathan and I talk on and off, I contacted him about this over
MSN. Jonathan, being the cool and
helpful guy that he is, immediately took the time to discuss this with me. I
coded away, told him what happened as I went along, and we brainstormed about
how we could solve it. After some work, we came up with something that seems to
work really fine, supporting class names with hyphens and multiple class names
on the same element. It is actually very similar to Jonathan’s original function
but with an escape fix and some performance add-ons.


Let me present The Ultimate getElementsByClassName:



/*
Written by Jonathan Snook, http://www.snook.ca/jonathan
Add-ons by Robert Nyman, http://www.robertnyman.com
*/

function getElementsByClassName(oElm, strTagName, strClassName){
var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
var arrReturnElements = new Array();
strClassName = strClassName.replace(/\-/g, "\\-");
var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
var oElement;
for(var i=0; i oElement = arrElements[i];
if(oRegExp.test(oElement.className)){
arrReturnElements.push(oElement);
}
}
return (arrReturnElements)
}

Some ways to call it



To get all a elements in the document with a “info-links” class.
getElementsByClassName(document, "a", "info-links");
To get all div elements within the element named “container”, with a “col”
class.
getElementsByClassName(document.getElementById("container"), "div",
"col");

To get all elements within in the document with a “click-me” class.
getElementsByClassName(document, "*", "click-me");

The first line in the function is to cover-up for a flaw in title="Internet Explorer">IE 5 where one can’t use the wildcard
selector * to get all elements. The rest is basically about setting up a regular
expression with the class name we’re looking for, where we escape hyphens and
then match that to the class names of the elements where we’re looking for
it.


Please try it out. Our hope is that it will help you develop unobtrusive
JavaScripts and that it will make it easier for you to maintain your web sites.
Any problems with the function, please let us know.


Go crazy now!


Updateded! Since Anne
asked for support
to look for multiple class names in the same call, I’ve
revised the function. The above function is intact and supports multiple class
names if they’re entered in that order on the element. If they’re not, you
should use the below function. Kudos to Curtis for inspiration.



function getElementsByClassName(oElm, strTagName, oClassNames){
var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
var arrReturnElements = new Array();
var arrRegExpClassNames = new Array();
if(typeof oClassNames == "object"){
for(var i=0; i arrRegExpClassNames.push(new RegExp("(^|\\s)" + oClassNames[i].replace(/\-/g, "\\-") + "(\\s|$)"));
}
}
else{
arrRegExpClassNames.push(new RegExp("(^|\\s)" + oClassNames.replace(/\-/g, "\\-") + "(\\s|$)"));
}
var oElement;
var bMatchesAll;
for(var j=0; j oElement = arrElements[j];
bMatchesAll = true;
for(var k=0; k if(!arrRegExpClassNames[k].test(oElement.className)){
bMatchesAll = false;
break;
}
}
if(bMatchesAll){
arrReturnElements.push(oElement);
}
}
return (arrReturnElements)
}

Ways of calling the function now are:



To get all a elements in the document with a “info-links” class.
getElementsByClassName(document, "a", "info-links");
To get all div elements within the element named “container”, with a “col”
and a “left” class.
getElementsByClassName(document.getElementById("container"), "div",
["col", "left"]);

Note that you can still use a string when only looking for a single class
name, but an array when looking for multiple class names.


Also, if this is to work in IE
5.0, you need to include this add-on to get support for the push method on the
Array object:



if(typeof Array.prototype.push != "function"){
Array.prototype.push = ArrayPush;
function ArrayPush(value){
this[this.length] = value;
}
}

If you don’t like to read the code here, you can download the
JavaScript file
.