getElementsByClass

The DOM provides a number of powerful methods for accessing nodes in a document. But there are also some surprising omissions.

For instance, although we have an insertBefore method, there is no corresponding insertAfter. Still, it’s possible to recombine existing DOM methods to plug the gaps in the DOM specs.

One of the most useful DOM methods of all is getElementById. It would be equally useful to have a getElementsByClass method.

Many, many coders have stepped up to bat on this issue by recombining getElementsByTagName("*") and className. The tricky bit is matching the value of className with the class you’re looking for. Remember that classes can be combined by separating them with spaces.

I came across a nice getElementsByClass function from Dustin Diaz:

function getElementsByClass(node,searchClass,tag) {
  var classElements = new Array();
  var els = node.getElementsByTagName(tag);
  var elsLen = els.length;
  var pattern = new RegExp("\b"+searchClass+"\b");
  for (i = 0, j = 0; i < elsLen; i++) {
    if ( pattern.test(els[i].className) ) {
      classElements[j] = els[i];
      j++;
    }
  }
return classElements;
}

It uses a regular expression to match the className which is perhaps a bit more resource-intensive than just using something like indexOf but it decreases the chances of getting false positives.

The function takes three arguments: node, searchClass and tag.

The node can simply be document if you want to search the whole document or you can pass it a result from getElementById (for example).

The searchClass parameter is the name of the class you want to match.

The tag parameter allows you to restrict the search to a specific tag. You can simply use the wildcard (“*”) to search all tags.

All in all, a nice little function.

Posted by Jeremy on Saturday, October 1st, 2005 at 6:29pm

Comments

There’s a getElementsByClassName() function defined in the WHATWG’s Web Apps 1 spec, which takes an arbitary number of arguments, each representing one class name. http://www.whatwg.org/specs/web-apps/current-work/#getelementsbyclassname

I’ve already got a prototype implementation that, AFAIK, fully conforms to the spec. I believe it’s still got some Safari bugs in it, but it works in Firefox, Opera and IE. http://lachy.id.au/dev/script/examples/DOM/DOM.js

# Posted by Lachlan Hunt on Saturday, October 1st, 2005 at 11:47pm

That RegExp is buggy. Try class="foo-bar" and getElementsByClass(document,"foo",*);

Use this instead: var pattern = new RegExp((^|\s)" + searchClass + "(\s|$));

# Posted by Lachlan Hunt on Sunday, October 2nd, 2005 at 5:08am

Oh, your comment system munged the code. There’s supposed to be 2 slashes before the \s, and I left out some quote marks. Hopefully it works this time.

RegExp("(^|\\s)" + className + "(\\s|$)");

# Posted by Lachlan Hunt on Sunday, October 2nd, 2005 at 5:12am

If you find yourself needing slightly more functionality than this, then Dean Edwards’ cssQuery is very useful. It lets you select elements in a similiar manner to getElementsByClass, except that instead of just class name, you can use any CSS selector. More details at:

http://dean.edwards.name/my/cssQuery/

# Posted by Dominic Mitchell on Sunday, October 2nd, 2005 at 10:02am

insertAfter would be the same as an insertBefore on the nextSibling, and if this sibling is non-existent, an appendChild seems to be performed automatically anyway. So, if more browsers would support it, adding insertAfter to a page would be very simple:

HTMLElement.prototype.insertAfter = function(element, insert) { insert.parentNode.insertBefore(element, insert.nextSibling); }

For IE support you could make it a global function (or linked to "document"), since the parentNode you perform it on can also be retrieved from the insert.parentNode… It’s like you said, the gaps are very plugable.

# Posted by peterned on Monday, October 3rd, 2005 at 8:35am

Hey Jeremy, thanks for posting this. This is Dustin ( found you in my rerrers list :) ). That’s great feedback about the foo-bar Lachlan. I must have skipped that possibility.

I agree Jeremy that it is possibly a bit resource intensive… I originally thought of doing a .split(’ ‘), but then you run a resource-intensive for loop nested in a for loop, which in general, I try to avoid loops nested in loops.

The cssQuery thing just came out like a week ago or so, so that’s a possible goodie as well.

# Posted by Dustin Diaz on Monday, October 3rd, 2005 at 5:42pm

I find it easier to make getElementsByClassName an object method. This way I can use it just like getElementsByTagName, while also reducing the number of required arguments the function needs to be passed.

Object.prototype.getElementsByClassName = function(searchClass) {

var els = this.getElementsByTagName("*");

// rest of function

}

e.g. var african_elephants = document.getElementById("africa").getElementsByClassName("elephant");

# Posted by Scott on Tuesday, October 4th, 2005 at 5:53am

Oh, I just learnt that you can do prototyping on DOM node constructors in Safari, so you don’t have to make getElementsByClass an object method (bad practise). Make sure you also add this to the Document prototype.

Document = document.constructor; HTMLElement = document.createElement("x").constructor;

Document.prototype.getElementsByClassName = HTMLElement.prototype.getElementsByClassName = function(class) {

// you know what

}

# Posted by Scott on Wednesday, October 5th, 2005 at 12:37pm

Sorry. Comments are closed.

October 2005
SunMonTueWedThuFriSat
      1
2345678
9101112131415
16171819202122
23242526272829
3031     

Recommended Reading

XML Subscribe

Grab the RSS feed for this blog.

JavaScript API

Grab the RSS feed of comments for this entry.