Say Hello to JavaScript’s Native getElementsByClassName

With the recent release of Firefox 3, we’ve now got a full fledged, mainstream browser that supports the long-awaited, often replicated, getElementsByClassName method. This is good news as we’ll all be just that much speedier as we ditch the various helper functions and go right into the browser for that functionality.

The thing is, the transition, when we’ll finally be able to make it, isn’t a one to one swap. In fact, depending on what you’re using it for you could run into problems transitioning from a helper function to the native method.

Why? Well, the native getElementsByClassName returns a NodeList and the functions an Array. This is a fundamental point as a NodeList, while it has a length and can be accessed by a numerical [index], isn’t an Array. One major difference, and the one that might cause serious problems if you’re not careful, is that it’s live. It doesn’t merely grab the returned value at the time of invocation, it updates its members as the document changes over time.

Here’s a simple example where I run the two versions side by side. Click start and both functions will count the number of “sample” classed elements in the document. A couple of seconds later I simply copy the innerHTML of the container DIV, doubling the number of “sample” elements on the page. As you then see a couple of seconds after that, the native code notes the adjustment to the document, the helper function does not. The counts are different.

Check it out (assuming you have a browser that supports getElementsByClassName):

Why is this important? Well, I don’t know about you, but in doing dynamic effects I often attach classes to elements so that (a) I can add effect-specific styles to them (duh) and (b) I can remember what state they’re in when I next run the function. A lot of times when a function is run I’ll take a snapshot of that collection and run some code like this on it:

var currSelected = getElementsByClass("sample");
		for (var i=0; i<currSelected.length;i++) {
			removeClass(currSelected[i],"sample");
		}

Which ensures that I’m starting with a clean slate by removing class “sample” from every element.

This is where it gets tricky. If I did that with the native getElementsByClassName I would get a weirdo result that looks like this. Click start and run the above code with getElementsByClassName in place of the helper:

For those of you without a compatible browser here’s a screen shot of the result:

With the helper function there would be no blue borders. Since getElementsByClassName is a live connection to the document, both the length and the elements at each index CHANGE as you remove elements from the document.

The reverse would be even worse. Adding classes to elements based on getElementsByClassName would create an infinite loop, since i will never be less than collection.length. Every time you add a new class the length increases by one and the loop never ends.

What does this all mean in practice? I’m thinking I’m going to keep my old reliable getElementsByClass function (or a variation that leverages the native method but returns an array “snapshot”) and selectively use the native method where applicable.

[edited] as was suggested in the comments, I wrote a new getElementsByClass to take advantage of the native code.

2 thoughts on “Say Hello to JavaScript’s Native getElementsByClassName

  1. Of course you could modify your code to look to see if the native function exists and if it does run that and copy the result into an array which is then returned.

    This way you get the speed of the native code with the immutable list quality of the existing code (which I have to admit I like, I had similar issues with getElementsByName one time before I twigged what was going on)

Comments are closed.