The ultimate getElementsByClassName, anno 2008

Two and a half years ago, I released the first version of getElementsByClassName. With how web browsers has evolved since, I thought I’d release a real ultimate version, dated 2008. :-)

Native web browser support

Safari 3.1 has native getElmentsByClassName support, and upcoming Firefox 3 and Opera 9.5 will have it too. It only leaves out, you’ve guessed it, Internet Explorer.

The necessity of this script

Living in a world where Microsoft is halting every possible way the web could evolve, this means that you will need a custom script for this for years to come. Also, the native version returns just a nodelist, as opposed to an array, which may somewhat limit your options to work with it.

New features

When I rewrote the script (download new version), there were a number of factors I wanted to include to make it the best script available for this task:

  • Utilize native getElementsByClassName support if it’s available.
  • Utilize native XPath support if it’s available.
  • Support multiple class names in the same call, in any order specified.
  • Return an actual array to work with, instead of just the native nodelists.

The way it works is that it detects the available native support, and then re-uses that for consecutive calls. This will lead top optimal performance for the end user experience. Please take a look at the demo page, which consists of a hefty 130 kb raw HTML, to compare execution times between call types, web browsers and platforms.

The script

The script should support basically any web browser being used today, and also has support back till IE 5.5. This is how the script now looks like:

/*
	Developed by Robert Nyman, http://www.robertnyman.com
	Code/licensing: http://code.google.com/p/getelementsbyclassname/
*/	
var getElementsByClassName = function (className, tag, elm){
	if (document.getElementsByClassName) {
		getElementsByClassName = function (className, tag, elm) {
			elm = elm || document;
			var elements = elm.getElementsByClassName(className),
				nodeName = (tag)? new RegExp("\\b" + tag + "\\b", "i") : null,
				returnElements = [],
				current;
			for(var i=0, il=elements.length; i<il; i+=1){
				current = elements[i];
				if(!nodeName || nodeName.test(current.nodeName)) {
					returnElements.push(current);
				}
			}
			return returnElements;
		};
	}
	else if (document.evaluate) {
		getElementsByClassName = function (className, tag, elm) {
			tag = tag || "*";
			elm = elm || document;
			var classes = className.split(" "),
				classesToCheck = "",
				xhtmlNamespace = "http://www.w3.org/1999/xhtml",
				namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace)? xhtmlNamespace : null,
				returnElements = [],
				elements,
				node;
			for(var j=0, jl=classes.length; j<jl; j+=1){
				classesToCheck += "[contains(concat(' ', @class, ' '), ' " + classes[j] + " ')]";
			}
			try	{
				elements = document.evaluate(".//" + tag + classesToCheck, elm, namespaceResolver, 0, null);
			}
			catch (e) {
				elements = document.evaluate(".//" + tag + classesToCheck, elm, null, 0, null);
			}
			while ((node = elements.iterateNext())) {
				returnElements.push(node);
			}
			return returnElements;
		};
	}
	else {
		getElementsByClassName = function (className, tag, elm) {
			tag = tag || "*";
			elm = elm || document;
			var classes = className.split(" "),
				classesToCheck = [],
				elements = (tag === "*" && elm.all)? elm.all : elm.getElementsByTagName(tag),
				current,
				returnElements = [],
				match;
			for(var k=0, kl=classes.length; k<kl; k+=1){
				classesToCheck.push(new RegExp("(^|\\s)" + classes[k] + "(\\s|$)"));
			}
			for(var l=0, ll=elements.length; l<ll; l+=1){
				current = elements[l];
				match = false;
				for(var m=0, ml=classesToCheck.length; m<ml; m+=1){
					match = classesToCheck[m].test(current.className);
					if (!match) {
						break;
					}
				}
				if (match) {
					returnElements.push(current);
				}
			}
			return returnElements;
		};
	}
	return getElementsByClassName(className, tag, elm);
};

How to call it and parameters

Parameters

className
One or several class names, separated by space. Multiple class names demands that each match have all of the classes specified. Mandatory.
tag
Specifies the tag name of the elements to match. Optional.
elm
Reference to a DOM element to look amongst its children for matches. Recommended for better performance in larger documents. Optional.

Call examples

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

Download the new getElementsByClassName

The new version is available for download right as we speak, so by all means, download it, take it for a spin, and experience selecting elements through their class names cross-browser, cross-platform without having to worry about any differences. :-)

Posted in Developing,JavaScript,Technology,Web browsers |

Leave a Reply

Your email address will not be published. Required fields are marked *