The Ultimate getElementsByClassName

New version released, with major updates Tuesday, May 27th, 2008.

NOTE: The code below is outdated.

A completely rewritten version of getElementsByClassName has been released, taking into account all new available web browser features and possibilities, to offer you the best performing and most flexible implementation. Read about the new version or go and download it straight away.

 

Updated on Tuesday, November 8th, 2005.

Updated again, Tuesday, November 8th, 2005
Switched from using word boundaries to checking for spaces and/or start/end of string. Also, see my comment about this.

Once again updated Tuesday, November 8th, 2005
I forgot a break; statement in the advanced version. Nothing that would break it, but bad performance-wise.

Updated Tuesday, November 8th, 2005
This is getting ridicilous now. Changed name of the linked JavaScript file to getElementsByClassName.js.

Updated July 14th 2006

As Wilfred and Sander correctly pointed out, there is a way to make the script more efficient in IE 5.x when using the wildcard character to call it. The code in this post and the downloadable file have been updated accordingly.

Updated May 11th 2007

It’s been a while since I took a look at this, and with speed comparisons and all, I have revised so it should be just a tad faster. The new script is just below and added to the downloadable JavaScript file as well.

One of the major differences is that tag name and containing element are optional, and if not supplied, will default to * respectively document. This means that the order of the parameters are also changed, so className to look for is the first one, followed by tag and then elm. However, for best performance, I recommend sending in all three parameters as closely specified as possible.

Updated May 30th 2007

The revised version in the file was ok, but the published version in the post just below missed a couple of \. This has now been adressed.

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<length; 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 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 Jonathan Snook 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<arrElements.length; 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 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<oClassNames.length; 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<arrElements.length; j++){
		oElement = arrElements[j];
		bMatchesAll = true;
		for(var k=0; k<arrRegExpClassNames.length; 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.

Posted in JavaScript |

247 Comments

  • Rowan Lewis says:

    Nice! Support for IE5 is more than I'd have ever asked for :)

    Btw, The design rocks :)

  • […] services with TiVo ZDNet UK: DoS attacks: The plague for our times? Robert’s talk: The Ultimate getElementsByClassName Digitoday: Oikotie oikoi mutkia […]

  • Jorkas says:

    Looks nice Robert, of course i will try it out!

    // Jorkas

  • amix says:

    The ultimate would be to use a JavaScript library – like http://www.mochikit.com/ or http://prototype.conio.net/. They offer similar functions + a lot more ;)

  • [..] tion like this in the past but was too lazy to write it. Should make my life easier. Ultimate getElementByClassName� [.]

  • Robert Nyman says:

    Rowan, Jorkas,

    Thanks!

    amix,

    Well, personally I don’t like using JavaScript libraries. Normally, they’re too bloated, and even if they’re not, it’s pretty much guaranteed that they will contain more code than needed.

    And from MochiKit’s web page:

    Our current test platforms include all of the modern and poular browsers: Safari 2.0.1, Firefox 1.0.7, Firefox 1.5b2, and Internet Explorer 6

    To me, it’s mandatory to make sure scripts work in IE 5 as well as earlier version of Mozilla-based web browsers like Firefox. Also, previously released versions of Safari are known to have some JavaScript issues.

    Curtis,

    Well, I can’t be on call all the time! :-)
    Thanks, Zilla, for the trackback then!

    couldn’t you use the RegExp "(^| )"+strClassName+"( |$)"?

    I tried that, but for some reason it didn’t work (although it should). Another way of putting it is "(^|\s)"+ etc.

    Anyway, word boundaries seems like the most correct approach, and since this seems to work fine, I didn’t investigate it further.

    andr3,

    That’s cool. I’m sorry you don’t like the layout, but I hope I can still write posts that are interesting enough to lure you back here! :-)
    Squint your eyes real hard and just focus on the white part with the text. ;-)

    Anne,

    Yes, of course it works with multiple class names. Otherwise it wouldn’t be of much use now, would it? ;-)

  • Instead of escaping the hyphen couldn't you use the RegExp <code>"(^| )"+strClassName+"( |$)"</code>?

    Also, it looks like amix forgot to close his anchor tag which made it nearly impossible to post this comment, lucklily the trackback from Zilla Smash ran interference.

  • andr3 says:

    Robert!

    Thank you both for doing this! :) I'll give this a test-drive and if i run into some relevant issues i'll get back to you.

    And since this is my first comment here since the redesign, i think i have to be honest with you (i'd like that if i had just redesigned my site). I'm not feeling this layout that much man. I'm sorry. Maybe it's the slightly pixeled image on top… I know it's being stretched, but that's exactly the problem. This is probably just me.. i just couldn't go without saying anything. ;)

    (this probably should go in the other post. sorry)

  • Does this take multiple class names into account? (See the <code>getElementsByClassName</code> definition in HTML 5.) If not, not sure if this is the ultimate solution ;-)

  • I tried that, but for some reason it didn’t work (although it should). Another way of putting it is "(^|s)"+ etc.

    Hmm… I have used that regexp in my own version of getElementsByClassName for a while now and haven't run into any problems.

    And while I don't want to speak for Anne — apparently he doesn't like that ;) — I believe he means multiple classnames in the function call as outlined in Web Applications 1.0

  • Robert Nyman says:

    Curtis,

    Hmm… I have used that regexp in my own version of getElementsByClassName for a while now and haven’t run into any problems.

    Yes, I don't know why it didn't work in my testing. Weird. But the word boundaries work! :-)

    Regarding Anne's comment, I just came back to the computer, thinking about it. I just tested it, and it works like a charm! :-)

    Only thing then is that the class names have to be in that order that you ask for them. For instance:

    <code>getElementsByClassName(document, "a", "info-links left");</code> will match <code><a class="info-links left">A tag</code>, but it won't match <code><a class="left info-links">A tag</code>.

    Not too much of an issue, if you ask me. If you use several class names on different elements, it feels pretty sensible to apply them consistently on all elements.

  • So it does not work. Thanks for clarifying. (Think of class names inserted randomly by some generator, you can not rely on order.) I already thought it did not cope with that as it seems you are checking the input against the value of the <code>className</code> DOM attribute. I only took a brief look at the code so therefore I asked.

  • Robert Nyman says:

    Anne,

    No problem.

    However, I don't think it would be to hard to tweak the regular expression to check for that too. But that's work for another day… :-)

  • I took a stab at matching, at least functionally, the getElementsByClassName described in Web Applications 1.0. I assume I interpreted the spec correctly — albeit loosely — and have come up with a solution, but then you know what happens when one assumes.

    Because it allows multiple classnames to be passed as either an array or space delimited string it should be completely backwards compatible with your function above (and many other implementations that follow that param pattern). It's a quick attempt and hasn't been thoroughly tested or optimized, so tear it apart and let me know what you find.

  • I take that back about being backwards compatible with your function above, I swapped the classname and tagname parameters to allow tagname to be omitted. Would be an easy modification however if backwards compatability was necessary.

  • Robert Nyman says:

    Curtis,

    Damn it, I thought about this during the morning and just sat down at the computer to code it. Of yourse you had to do it then! :-)

    Your solution was pretty identical to mine. I've updated the post with an alternative where it works. Thanks for the inspiration!

  • Yes, I don’t know why it didn’t work in my testing. Weird. But the word boundaries work!

    Regexp with word boundaries will match 'info-links-big' while searching for 'info-links' (hyphen is considered as word boundary), while it shouldn't.

  • Robert Nyman says:

    Bruce,

    Thank you for that invaluable feedback!

    The scripts and the post are now changed to using <code>"(^|s)"</code> and <code>"(s|$)"</code> instead.

    Also, I found out why that approach didn't work for me before: the check for spaces and start/end fo string has to be enclosed within parentheses when creating the RegExp object.

  • Curtis says:

    Sorry Robert, I was stuck at the office yesterday and gave it a shot. Our solutions are very similar but…

    * since you are no longer using word boundaries, why escape the hyphens?

    * once one RegExp match fails there is no reason to check the remaining, so <code>break</code>ing out of the loop might save a wee-bit of time.

    Also I see you are requiring multiple classnames to be passed as an array, which is probably a closer interpretation of the spec but I just like the syntactic sugar of passing a space delimited string (which looks as though is still be debated within WhatWG). All-in-all I think we have a pretty good solution here, but I am hoping Anne chimes in to confirm/deny this.

  • Curtis says:

    Apparently I have too much time on my hands, but I noticed you named the JS file getElementsByTagName.js, I assume that is a mistake? Oddly enough I did the same thing yesterday.

  • Robert Nyman says:

    Curtis,

    It's ok. :-)

    since you are no longer using word boundaries, why escape the hyphens?

    I haven't actually tested without escaping the hyphens, but I was afraid that it wouldn't work since hyphens have a special meaning in regular expressions (setting a span of characters to look for, e.g. A-Z).

    once one RegExp match fails there is no reason to check the remaining, so breaking out of the loop might save a wee-bit of time.

    I was apparently sloppy; of course there should be a <code>break;</code> statement there. It's added in the code and post now.

    …but I just like the syntactic sugar of passing a space delimited string…

    I thought a little about using a space delimited string and splitting it into an array, but just figured this was a more correct approach.

    However, I do agree that if Anne's reading this, it would be interesting to hear WHATWG's opinion about this.

    Apparently I have too much time on my hands, but I noticed you named the JS file getElementsByTagName.js, I assume that is a mistake?

    It's totally ok with your comments, I really like this constructive discussion! :-)

    I did name the file that. Maybe I'm missing something here, but why wouldn't it be appropriate? If it becomes a built-in function in the future or because it's the name of the function?

  • Curtis says:

    I did name the file that. Maybe I’m missing something here, but why wouldn’t it be appropriate? If it becomes a built-in function in the future or because it’s the name of the function?

    Shouldn't it be getElementsByClassName.js instead of getElementsByTagName.js? ;)

    It’s totally ok with your comments, I really like this constructive discussion!

    Good, me too.

  • Curtis says:

    I haven’t actually tested without escaping the hyphens, but I was afraid that it wouldn’t work since hyphens have a special meaning in regular expressions (setting a span of characters to look for, e.g. A-Z).

    A hyphen only has special meaning for RegExp when used in a character class, [].

    I didn't realize this at the time, but by not escaping hyphens–or any special characters–you could use a regular expression pattern for the classname.

    Example using my getElementsByClassName:

    * <code>getElementsByClassName(document, '.+-.+');</code> would match any element belonging to any hyphenated class.

    * <code>getElementsByClassName(document, "b{3}.*");</code> would match any element belonging to any class beginning with "bbb".

    An interesting side-effect, but could introduce some bugs, although no other valid className characters (unresearched) immediately come to mind that have special meaning within RegExp.

  • Robert Nyman says:

    Curtis,

    Shouldn’t it be getElementsByClassName.js instead of getElementsByTagName.js? ;)

    Ha ha! That is freaky!

    Force of habit, I guess… :-)

    …by not escaping hyphens–or any special characters–you could use a regular expression pattern for the classname.

    Interesting. For now, I think I'll leave the hyphens in there, but when I feel crazy, I'll remove them and test it thoroughly.

    Interesting, though, to pass in a regular expression looking for class names. Probably overkill, but cool! :-)

  • andr3 says:

    <blockquote cite="http://www.robertnyman.com/2005/11/07/the-ultimate-getelementsbyclassname/#comment-1573">andr3,

    That’s cool. I’m sorry you don’t like the layout, but I hope I can still write posts that are interesting enough to lure you back here! :-)

    Squint your eyes real hard and just focus on the white part with the text.

    Man, that's something i'm not worried about… I'm not going anywhere. Not with the quality of such posts like this one. You rawk!

    Great job you guys. Haven't had time to try it though. :( But i'll definately use it in the future.

  • Robert Nyman says:

    andr3,

    Thank you, I'm happy to hear that! :-)

  • Does it come with an 'update available' link included :)

  • Robert Nyman says:

    Steve,

    Ha ha! Hopefully it works now and shouldn't need any more updates.

    The only thing I can think of, off the top of my head, is to try and remove the escaping of the hyphens. But it shouldn't be necessary, and for now I keep them in there to play it safe… :-)

  • […] tes where you can download some of the top designs from OSWD: (tags: oswd templates) Robert’s talk » The Ultimate getElementsByClassName After some wor […]

  • Adam van den Hoven says:

    Robert,

    Great work. The only thing I would love to see would be adding this to the DOM interfaces. Basically, I'd like to see:

    <code>

    document.getElementById("SomeElement").getElementsByClassName( "div", "someclass" )

    </code>

    Now, that would be cool.

  • Robert Nyman says:

    Adam,

    From the top of my head, you can use this in Mozilla web browsers:

    <code>

    HTMLElement.prototype.getElementsByClassName = function (strTagName, strClassName) {

    getElementsByClassName(this, strTagName, strClassName);

    };

    </code>

    However, I'm not sure if this works in Safari or how to accomplish it in IE without looping through all the elements and adding the method to them.

  • John says:

    Instead of using <code>"(^|s)" + strClassName + "(s|$)"</code> might I suggest <code>"" + strClassName + ""</code>.

  • Robert Nyman says:

    John,

    No, you might not! :-)

    Seriously, though, I was using word boundaries at first, it seemed like a more elegant approach. But as Bruce pointed out, it became an issue when using hyphens in class names.

  • […] ma entrega… Comenzamos con cosas de Javascript y DOM… En Robert’s Talk, una funci�n ge […]

  • Ed says:

    This is good. Why isn't there a getElementsByClassName in JavaScript by default? Did the developers do getElementsByID one day and get tired, so they just forgot it?

  • Ed says:

    oh, sorry about the code!

    You can see how I’m using it at
    this sample page.

  • Ed says:

    sorry, I should also say that my sample page is based on another sample page, first linked from a web design thread in flickr.

  • Ed says:

    Anything wrong with doing things this way instead?

    function tagHasClass(theTag, theClass)

    {

    var returnValue = false;

    var allClasses = theTag.className.split(" ");

    for(i=0;i

  • Robert Nyman says:

    Ed,

    Thanks!

    I have no idea why it wasn't included initially with getElementById and the rest.

    Regarding your code example: I'm not really sure what you were going for, but if you want to have code in a comment, please remember to escape < with &lt; and > with &gt;.

  • Robert Nyman says:

    Ed,

    No problem. :-)

    And sure, splitting class names can also be an approach.

  • lamberete says:

    WOW!

    Great script! just what I was looking for.

    I've tested it on Konkeror 3.4.3 and it works perfectly!

    From now on, I'll come here from time to time ;)

  • christian says:

    What is the difference/advantage of this method over something like Simon Willison's getElementBySelector?

  • Robert Nyman says:

    christian,

    Well, I haven't studied Simon's getElementBySelector carefully, so he'll have to excuse me if I'm incorrect here.

    First, this function is more light-weight

    Second, if I'm not mistaken, his function loops through all the elements in the document for every check, which can decrease performance, especially when it comes to a large document.

    Third, I just find this to be a more easily understood approach than sending selectors as a parameter to the function.

  • christian says:

    Robert,

    Your points are well taken. I particularly agree with you on the 2nd.

    Still, there is something to be said for the specificity of using selectors as opposed to just a class name.

    This seems like a case of picking the right tool for the job.

    Thanks for adding to the toolbox. :)

  • Robert Nyman says:

    christin,

    Absolutely, I agree. The power of specificity should definitely not be underrated. :-)

  • louis w says:

    how can i override the prototype version of this with yours?

  • Robert Nyman says:

    louis w,

    I'm not sure how prototype is built up, but I guess you can just include my JavaScript file separately or include the <code>getElementsByClassName</code> function within the prototype JavaScript file.

  • louis w,

    In prototype, <code>getElementsByClassName</code> is a method added to the document element. Robert's is a standalone function. If I'm not mistaken, the two following calls return the same array:

    <code>getElementsByClassName(document, '*', 'foo');</code>

    <code>document.getElementsByClassName('foo');</code>

  • Robert Nyman says:

    Chuck,

    Yes, I believe those two will be the same.

  • […] commentaire » Un Back to top. » The Ultimate getElementsByClassName.   Catégories: java […]

  • Henrik says:

    Congratz Robert :) You're on stylegala in the news section!

  • Robert Nyman says:

    Henrik,

    Thanks! Actually, I have two items on Stylegala's first page for the moment… :-)

    A very rare thing, I know!

    I've been on it a couple of times, but it's an honor each time.

  • […] älkomnas. Metoden för att välja ut element kan ocksÃ¥ förbättras. Robert Nyman har en getElementsByClassName funktion som man kan implementera.

    Det här inl&aum […]

  • Dustin Diaz says:

    With my getElementsByClass I wrote a few months back, I switched the order of the names for cases of when the developer wishes not to declare a tagName or a parentNode. It seems more practical that way. Other than that, it's nice to add another getElementsByClassName to the growing pile.

    As you can see, you, snook, and myself are all using the same RegExp :)

  • Robert Nyman says:

    Dustin,

    Ah, right, it seems to be popular! :-)

  • David says:

    You should add support for Moz's XPath which will greatly speed things up.

  • Robert Nyman says:

    David,

    That's an interesting idea, and I might do that in the future. Personally, I'm a big fan of XSLT and XPath, so I'm really happy with where things are going. :-)

  • One system we've setup is using css selectors, such as

    getElementsByClassName("div.myclass")

  • Robert Nyman says:

    Micah,

    Yes, I know people who prefer that approach. Personally, I prefer the one in the script above, since it feels more clear from a programmatical point of view as opposed to thinking CSS selectors.

  • Diego says:

    HI!

    Great function! (it's a bit sad that you cannot add it to the Element.prototype, it would be nearly "perfect")…

    just a curiosity…

    why you didn't use the Element.all property to speed up search in case of IE?

  • Robert Nyman says:

    Diego,

    Thank you! :-)

    why you didn’t use the Element.all property to speed up search in case of IE?

    You mean instead of <code>getElementsByTagName</code>? I wanted to use standard DOM methods as far as possible, and I haven't experienced any performance lack because of that.

  • Jimmy says:

    Thank you, thank you, thank you. This function solves a major problem I've been having . . . and it makes my life so much easier. Awesome function!

  • Robert Nyman says:

    Jimmy,

    No problem, I'm glad it helped! :-)

  • […] ’s version and then shaped by valuable feedback from talented people (please see the ultimate-getelementsbyclassname) , if(typeof whatEver != “undef […]

  • I like it, BUT…

    I think the argument order is all wrong. I doubt that you will change it (who wants to break everyones code?), but I think that it could really benefit as far as actually using the function and using it quickly.

    For instance, I would venture to say at least 90% of the time people use this, they want to search for all tags by a certain class in the document.

    So why have the most used part of the function as the last argument?

    If I had my druthers, the argument order would look something like this:

    getElementsByClassName(oClassNames, strTagName, oElm );

    Why? Because I think it should go in order of frequency of use, as well as specificity.

    oClassName will get you every element with that class in the document.

    strTagName will get you the specific tags with that class in the document.

    and oElm will let you narrow your search even further by specifying the element to search in.

    I would just have strTagName default to * and oElm default to document.

    This way, if you only want to grab everything in the document with a certain class you just say:

    getElementsByClassName('testClass');

    and if you want to narrow it even further, you can add specific parameters.

    Okay, that's a lot of rambling :)

    Good work even with my gripes :)

  • Robert Nyman says:

    Nate,

    I agree with you points, very valid ones. However, I guess one good thing that can come out of having to specify a tag name and what element to look in is to force people to think more about performance.

    As you said, though, I won't change it! :-)

    But, in the end, the function's here for people to tweak and use whichever way they want to, so feel free to adapt it to your own personal preferences and needs.

  • l0b0 says:

    I've tried to use this code, but when passing the resulting list to a function, I get an error message when running getAttribute on one of the elements. There's a thread over at CodingForums, but maybe someone here has had a similar experience?

  • Robert Nyman says:

    l0b0,

    I just took a very quick glance at the code but didn't directly see why it didn't work. <code>getElementsByClassName</code> just returns an <code>array</code> of elements, so the problem seems to lie in your <code>toggle</code> function.

    What if you made the array helpElements into a global variable and then referenced it?

  • Robert Nyman says:

    l0b0,

    I’m sorry, but that hasn’t got anything to do with what’s returned from getElementsByClassName : they are just pure object references.

    Try working with that array directly; loop throgh it and just alert what you have in every position and you will see it.

  • l0b0 says:

    Robert,

    There's been some progress, but I'm still in the dark. The objects passed from getElementsByClassName don't seem to be referencing the elements in the DOM once passed to a function; I only get "undefined" for properties such as nodeName and parent.childNodes.length.

  • Awesome script! Can you tell me what the license is on this? I'm including a credit comment at the top of the script before I use it, and I'd like to note any license terms there as well.

  • Robert Nyman says:

    Adam,

    Thanks! :-)

    The credit comment is all it takes, no license. Go crazy with it!:-)

  • Duly noted, Robert, and thanks for the reply.

    Those who find this script useful might also be interested in something the guys at Mad4Milk recently cooked up: moo.dom, a slim bit of JavaScript that pretty much lets you go crazy with simple CSS selectors and tack on actions to the selected elements in one fell swoop. The demo page they created gives you a pretty good idea of how it works in practice.

    The drawback of moo.dom is that it’s a bigger script than Robert’s “Ultimate getElementsByClassName,” especially when you factor in the requirement of a stripped-down version of prototype.js. The total file weight of moo.dom.js + prototype.lite.js is just under 6 KB. By comparison, the second version of Robert’s script is about 1.3 KB with the IE5 compatibility patch included.

    Basically, moo.dom is good if you’d like a somewhat more intuitive selection syntax married to easily-added actions or effects and your page isn’t already too heavy with script. If you want something more lightweight that can easily be incorporated into your own scripts instead of acting as a one-shot solution, then Robert’s script is a better bet.

  • Robert Nyman says:

    Adam,

    Thanks for the tip!

  • gorbiz says:

    I love it!

    I almost always write my code myself, thou this can not be improved and should not be modified. (:

    Thanks!

  • Robert Nyman says:

    gorbiz,

    Thank you! :-)

  • […] les and then decided to do my own version for fun. Out of all of the functions I looked at Robert Nyman’s came closest to what I wanted (kudos!) but my ver […]

  • […]    IE/Win 5+, IE/Mac 5.2+, Firefox 1.0+, Opera ?, Safari ?     * getElementsByClassName (parent, tag name, class name)   &n […]

  • […] ’s version and then shaped by valuable feedback from talented people (please see the […]

  • Michael Winter says:

    I realise that escaping class names has been discussed previously, but the String.prototype.replace call is still present. Whilst class names may contain special characters, from the perspective of the regular expression, a hyphen isn’t one of them.

    On a different subject, at the very least,

    <code>Array.prototype.push = ArrayPush;

    function ArrayPush(value){

      this[this.length] = value;

    }</code>

    should be replaced with:

    <code>Array.prototype.push = function(value){

      this[this.length] = value;

    };</code>

    The introduction of an identifier is unnecessary. However, this is still undesirable as it unconditionally substitutes a native implementation for an inferior alternative.

    Better would be:

    <code>if (typeof Array.prototype.push != 'function') {

      Array.prototype.push = function(value){

        this[this.length] = value;

      }

    }</code>

    Better still (in my opinion):

    <code>if (typeof Array.prototype.push != 'function') {

      Array.prototype.push = function(v) {

        var i = this.length >>> 0,

            j = 0;</code>i

        while(j

    The latter behaves exactly as described in section 15.4.4.7 of ECMA-262, 3rd Ed.

    Mike

  • Michael Winter says:

    Oops. Let’s try that again.

    <code>if (typeof Array.prototype.push != 'function') {

      Array.prototype.push = function(v) {

        var i = this.length >>> 0,

            j = 0;</code>

    <code>    while(j < arguments.length) {this[i++] = arguments[j++];}

        return this.length = i;

      };

    }</code>

    Apologies,

    Mike

  • AndrÃ&A says:

    <code>function getElementsByClassName( _tag, _class, _scope){

    var regexp, classes, elements, element, returnElements;</code>

    <code>_scope = _scope || document;</code>

    <code>elements = !_tag || _tag == "*" ? document.all : _scope.getElementsByTagName(_tag);

    returnElements = [];</code>

    <code>classes = _class.split(/s+/)

    regexp = new RegExp("(^|s+)("+ classes.join("|") +")(s+|$)","i")</code>

    <code>if(_class){

    for(var i=0; element = elements[i]; i++){

    if(regexp.test(element.className)){

    returnElements.push(element);

    }

    }

    return returnElements;

    }else{

    return elements;

    }

    }</code>

    Usage:

    var inps = document.getElementsByClassName("div", "menu menu-top", $("menu"))

  • goetsu says:

    anybody use this script with opera 9 ? for me it doesn't work.

  • wilfred nas says:

    Robert (or as you are on leave, Anyone),

    a co-worker spotted a possible mistake. We think that

    <code>

    var arrElements = (strTagName == "*" && document.all)? document.all : oElm.getElementsByTagName(strTagName);

    </code>

    Should be:

    <code>

    var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);

    </code>

    As that it should only search in the original context, not the whole document..

    just our four (2×2) cents

    Wilfred and Sander

  • pike says:

    I may be mistaking, but the use of document.all in the script suggests it only works in explorer, doesn't it ? if so, it's not quite ultimate yet ;-)

  • Robert Nyman says:

    Michael Winter,

    Thanks for your suggestion.

    André,

    You're welcome to take a stab at your own implementation anytime. :-)

    goetsu,

    For me it works in Opera 9, so you might have some error in the way you call it.

    Wilfred and Sander,

    You are absolutely right! Thanks for pinting that out, the post and the code has been updated! Now it should be more efficient for for IE 5.x as well.

    pike,

    It only uses <code>document.all</code> for IE 5, since IE 5.x has a flawed implementation where it doesn't support the wildcard character. As it says in the post:

    The first line in the function is to cover-up for a flaw in IE 5 where one can’t use the wildcard selector * to get all elements.

  • Robert Nyman says:

    Chi-Wai Lau,

    The second function in the post should work with multiple class names; at least it does so for me. Make sure that's the one you're using.

    Good luck!

  • Robert Nyman says:

    Chi-Wai Lau,

    You need to escape HTML characters, i.e. &lt; for < and so on.

    But I get your drift how you made it work, but I don't know why the function doesn't work in the first place for you then.

    Did you make sure you're using square brackets around the multiple class names, to create an array? It should look something like this:

    <code>["col", "left"]</code>

  • Robert Nyman says:

    Chi-Wai Lau,

    No problem.

    Hmm, if c1 and r1 are variables that just contains strings, then it should work… If that's not the problem, then I'm sorry, I can't figure out what it would be.

  • Robert Nyman says:

    Chi-Wai,

    I just tested in Safari 2.0.4 and it worked fine for me, and it also worked if I specified variables and then used them in the function call. What happens when you try it? Do you get an error, or is the length just 0 for the array returned?

    I also recommend that you try it in Firefox and check the JavaScript Console there for eventual errors (Tools > JavaScript Console).

  • […]- Risk the game! I cant wait for the multiuser ver. (tags: ajax api web2.0 google game) The Ultimate getElementsByClassName Why this d[…]

  • […] ts which may be a combination of XPath, XQuery, CSS syntax, and simple new methods such as getElementsByClassName. Add/Remove Class Prototype has it, JQuery […]

  • Sander Bol says:

    Rovert,

    Thanks for putting this online – saved me a bunch of headaches and time. Clean, simple and efficient. Hurray for sharing.

    –S.B.

  • Sander Bol says:

    Robert,

    Thanks for putting this online – saved me a bunch of headaches and time. Clean, simple and efficient. Hurray for sharing.

    –S.B.

    Doh: Typo in your name. Shame on me. Feel free to remove previous post.

  • Robert Nyman says:

    Sander,

    I'm glad it helped. And don't worry about the spelling… :-)

  • Vlad Fratila says:

    Thank you for this, its support on IE is really great!

  • Robert Nyman says:

    Vlad,

    No problem, I'm happy you like it. :-)

  • This isn't working for multiple class names on one element:

    class='note nopromo'

    Maybe this line could be changed:

    var pattern = new RegExp('(^|s)' + searchClass + '(s|$)');

    I'm a bad regex-er, or I would offer a solution.

  • Robert Nyman says:

    Matt,

    It works if the element has multiple class names, but it doesn't support looking for several class names with the same call.

  • […] e instead of the built in one provided by prototype was created by Robert Nyman called the Ultimate getElementsByClassName. He’s done a great job explainin […]

  • […] e instead of the built in one provided by prototype was created by Robert Nyman called the Ultimate getElementsByClassName. He’s done a great job explainin […]

  • […] A function to get all elements with a certain class name. More thoroughly described in The Ultimate getElementsByClassName. addClassName/dt A functio […]

  • Andy says:

    How about adding wildcard support to the class names ?

  • Robert Nyman says:

    Andy,

    I've thought about it, but didn't find it that important of a feature. Naturally, though, you can easily tweak the script if you want that.

  • Andy says:

    Yeah well, I discovered that it worked fine using regex statements so that's alright.

  • […] JavaScript com diversas implementações. Aprecio as versões de vários autores como a do Robert Nyman e a do Dustin Diaz, porém prefiro utilizar uma que dese […]

  • Simz says:

    Good function!

    But doesn't work on IE 5 Mac…

    My IE 5.1 Mac doesn't support array.push() so i replace the push method by

    array[array.length] = new regexp…

    Now its working…

    The returned elements are ordered Desc so you need to reverse it

    BTW IE 5.2 is supporting Array.push()?

    thx

    SImon

  • Kenneth says:

    Couldn't get it to work when using arrays. Wrote this for-loop instead:

    <code>

    for(var j=0; j<arrElements.length; j++){

    oElement = arrElements[j];

    for(var k=0; k<arrRegExpClassNames.length; k++){

    if(arrRegExpClassNames[k].test(oElement.className)) {

    arrReturnElements.push($(oElement));

    break;

    }

    }

    }

    </code>

  • Robert Nyman says:

    Simz,

    In the end of the post, there's code for implementing support for <code>push</code> in IE 5.

  • Dennis says:

    worked great! thanks!

  • Soylent says:

    you could also use <code>""+strClassName+""</code> for the regular expression

     (bareword) will match word boundaries as well as the beginning/end

    reference

  • Soylent says:

    Sorry, make that: <code>"b"+strClassName+"b"</code>

  • Robert Nyman says:

    Soylent,

    As mentioned at the top of the article, word boundaries won't work when there are hyphens in a className.

  • Soylent says:

    Okay, this thing is killing my double backslashes. So, that should be 2 backslashes before the b.

  • […] updated to reflect that changes that’ve been made. getElementsByClassName has long been a mainstay of web developers everywhere – and by making it official (bot […]

  • […] ClassName como parte de su core. Esto es una gran noticia ya que no tendremos que realizar el apaño mediante javascript y dispondremos de una funcionalidad inte […]

  • […] entsByClassName. Fingers crossed the other browsers follow suit soon, so we can stop using 20 li […]

  • Simon says:

    Robert,

    a very useful bit of code – thanks to it, I've managed to put together a method of expanding elements vertically.

    Thanks!

  • illimar says:

    does anyone know how i could use this piece of code to show/hide layers of certain class?

  • joncl says:

    I was putting together an eBay ad that needed this script (great script by the way!). Come to find out, eBay will not allow the replace() function. So I just removed the function, refactored slightly, and this is now working for me in my eBay ads (given there's no hyphens in the class names of course).

    <code>

    function mcsGetElementsByClassName(oElm, strTagName, oClassNames){

    var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);

    var arrReturnElements = new Array();

    var arrRegExpClassNames = new Array();

    var oClassNames2 = (typeof oClassNames == "object") ? oClassNames : [ oClassNames ];

    for(var i=0; i

  • joncl says:

    try again…

    <code>

    function mcsGetElementsByClassName(oElm, strTagName, oClassNames){

    var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);

    var arrReturnElements = new Array();

    var arrRegExpClassNames = new Array();

    var oClassNames2 = (typeof oClassNames == "object") ? oClassNames : [ oClassNames ];

    for(var i=0; i

  • Robert Nyman says:

    Simon, joncl,

    I'm glad it was of use to you!

    joncl,

    You need to escape < with &lt; and > with &gt; to make it work.

  • […] liminate the library dependence. You do need a getClass and event handler function. I used getElementsByClassName by Robert Nyman and John Resig’s addevent […]

  • Babette says:

    I have a rather strange question, one I hope will not offend you – do you know where your last name originated from? I am doing research into my family line and Nyman came up – could you help?

    sorry to bother you :-( ,but please answer – thanks

  • […] tsByClassName function. Luckily, though, I was spared that horrible fate, because I found the Ultimate getElementsByClassName, courtesy of a guy by the name of […]

  • […] ling or tripling page load times in IE6. Workaround: Use an optimized version. cf. The ultimate getElementsByClassName Filed u […]

  • […] de();     }   }   return outArray; } The Ultimate getElementsByClassName Uses a pure DOM implementation, t […]

  • Robert Nyman says:

    Babette,

    Ha ha, don't worry! :-)

    To be honest, I don't really know. Nyman literally means "new man" in Swedish (and I'm, naturally then, Swedish), but that's all I know about it.

  • […] = treeWalker.nextNode(); } } return outArray; } The Ultimate getElementsByClassName [url] function getElementsByClassName(oElm, strTagName, strClassN […]

  • […] he link is clicked first gets some elements through a getElementsByClassname function (try The Ultimate getElementsByClassName or DOMAssistant’s core file […]

  • […] cade-prosessin mukaisesti. Vinkki: luokkia voi käyttää tyylin määrittämisen lisäksi XHTML- ja HTML-elementtien käsittelyyn Javascriptillä. 8. Mozilla-sp […]

  • Thank for this great stuff which get my LiFE-LiNE project possible ! :o)

  • […] The Ultimate getElementsByClassName […]

  • murdos says:

    Really great function !!

    Could you clarify the licence (GPL, BSD, …) you're releasing it?

    It could be strange to ask this, but I want to use it in GPL code and depending on the licence you've choosed it may be an issue (for me ;-) ).

    Thanks :D

  • Robert Nyman says:

    murdos,

    Acutally, no license and it’s free to use. If I had released it under a license, back in the day, it would have been the Creative Commons Attribution-ShareAlike 2.5 license.

  • […] a small note to those of you who use my getElementsByClassName script: it has been updated for performance reasons and flexibility […]

  • Nice one. Thanks for the code. I have actually used it a couple of times. Keep up the good work.

  • Robert Nyman says:

    James,

    Nice, I'm glad that you like it! :-)

  • […] qui va ré-afficher les blocs cachés. Ce qui est trés facile à faire en utilisant la ressource getElementsByClassName* de Jonathan Snook et Robert Nyman… Ces blocs seront donc visibles seulement si le navigateur […]

  • Good Stuff. Pure Plug and Play!

    Thanks!

  • Enoch Carter says:

    I've made a slight modification to your latest example that suports multiple class names, I had to search through elements that contained multiple class names but only select thoes that contain at least on the the class names I'm searching on. that may sound confusing but the solution I found is simply this:

    for(var j=0; j<arrElements.length; j++){

    oElement = arrElements[j];

    bMatchesAll = false;

    for(var k=0; k<arrRegExpClassNames.length; k++){

    var classname = oElement.className.split(" ");

    for(q=0;q<classname.length;q++){

    if(arrRegExpClassNames[k].test(classname[q])){

    bMatchesAll = true;

    break;

    }

    }

    }

    if(bMatchesAll){

    arrReturnElements.push(oElement);

    }

    }

  • Robert Nyman says:

    Enoch,

    Thanks for sharing.

  • Tad says:

    Looks like you have the documentation on your function incorrect…the first attribute is the class, the second is the tag and the third is the document object

  • Robert Nyman says:

    Tad,

    As is written at the top of the post, the updated version has a changed order for calling. You can choose to use ny of the old versions or the new one.

  • […] As a side note: I originally got parts of this code from Robert Nyman’s Ultimate getElementByClassName post. Filed under: JavaScriptBy Sid Roberts On May 28, 2007 At 6:24 pm Comments : […]

  • crash says:

    Hello,

    I've wrote my own getElementsByClassName, and a can say it's the ultimate one. It support Browsers who come with native getElementsByClassName-support (already in Firefox3), and it uses FAST FAST FAST XPath to get the nodes.

    You can find it here: <a&gt <a href="http://;http://phpfi.com/238008” target=”_blank”>;http://phpfi.com/238008
    For some browsers (like Opera) you need also the following: <a&gt <a href="http://;http://phpfi.com/238014” target=”_blank”>;http://phpfi.com/238014

    Thanks

  • […] när sidan laddats. highslidealiceImages är en aningen omskriven och anpassad version av Robert Nymans utmärkta getElementsByClassName som ser till att alla bilder med klassen »highslide« initieras och blir klickbara. […]

  • […] semplice e di banale utilizzo. Se le esigenze fossero diverse, Robert’s Talk, nel suo blog, ha raccolto tutta una serie di funzioni per ricavare gli elementi a partire dal nome […]

  • gfed says:

    Hey, crash.

    What would it take to make your code work in IE?

  • gfed says:

    Crash,

    I thought EVALUATE was considered to be EVIL.

    gfed

  • Crash says:

    gfed: You need indexOf for Arrays, see: http://phpfi.com/238014
    document.evaluate() isn't eval() they doesn't do anything similar.

  • […] The Ultimate getElementsByClassName […]

  • Seth says:

    I am having a problem with the function. When called within another function I get error:

    elm.getElementsByTagName is not a function

    and it points to this line:

    var elements = (tag == "*" && elm.all)? elm.all : elm.getElementsByTagName(tag);

    Any idea what is going on?

  • Robert Nyman says:

    Seth,

    Depending on what version you use of the function (there are two), you shall either send in an object reference as the first parameter, or (optionally) send in an object reference as the third parmater (this goes for the revised version of the function, at the beginning of this post).

    An object reference looks something like this:

    <code>document.getElementById("container")</code>

  • Seth says:

    I called the function like this:

    sum = getElementsByClassName(document, "input", "t_month");

    Is the correct way to do it? I appriceate your quick response and general helpfulness!

  • Seth says:

    Nevermind, I got it! I was passing object and class backwards! Thank you again for your efforts toward writing this VERY helpful function! I wonder why it was overlooked when getElementById and TagName were conceived? Thank you, thank you, thank you!

  • Robert Nyman says:

    Seth,

    Good that it worked out for you! I was a bit worried there for a while… :-)

  • Ann says:

    Hi there!

    I am glad to find the getElementsByClassName function – it's really helpful. Thank you for it! :)

    But I have a question about regular expression which is used in both versions.

    As I know regular expression lets you search a string for the first matching pattern and test(param) returns true if it finds the regular expression in param.

    If so, why can't we just use testClass = new RegExp(strClassName)? When used with test(param), just like in your function, it would check for first match of strClassName in the object's className:

    if(oRegExp.test(oElement.className)){

    arrReturnElements.push(oElement);

    }

    Thank you for your answer! :)

  • Robert Nyman says:

    Ann,

    Thank you! It's because other class names might contain the exact same text and more, and you don't want to match partial words. E.g. if you're looking for elements that have the class valid you don't want to match the term validation if some other class has that.

  • Ann says:

    Thank you for your answer! :) I've just tried to understand it on my own and write that now I know ;)

    Once again thanks for great function :D

  • Chris Phillips says:

    This my version. Supports a negated className lookup.

    //supports matching all classes other than the one passed like !myClassName

    function getElementsByClassName(className, tag, elm){

    var neg = '';

    var testClass = null;

    if(className.charAt(0) === '!'){

    testClass = new RegExp('(^|s)(?'+className+')w+(s|$)')

    className = className.substring(1,className.length);

    } else {

    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 < length; i++){

    current = elements[i];

    if(testClass.test(current.className)){

    returnElements.push(current);

    }

    }

    return returnElements;

    }

  • Robert Nyman says:

    Chris,

    Interesting approach with the negated className route!

  • Dima says:

    Hi, I think it's imperative to have an optional call back here! What's the typical usage of this function? to retrieve an array of elements and then do something to them. So you've just gone through a for() loop to get the array and now you have to go through another loop to do whatever it was you wanted to do.

    So it would be something like this:

    <code>

    function getElementsByClassName(className, tag, elm, callBack){

    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<length; i++){

    current = elements[i];

    if(testClass.test(current.className)){

    if(callBack)callBack(current);

    returnElements[returnElements.length]=current;

    //returnElements.push(current);

    }

    }

    return returnElements;

    }

    </code>

    also I've done a little timing test and appending items to an array using .length is a little bit faster (in FF)

  • […] CSS class, however. To help with this, I found a nice JavaScript function called getElementsByClassName() by Jonathan Snook and Robert Nyman. Calling this function placed all the DIV objects with the […]

  • Ivan says:

    Hi everyone. Sorry for the long comment. This getElementsByClassName script is great, but it just doesn't seem to be recognized by Blogger. I only know a little JS, so I need some help. Here's the code. Explanation follows.

    <code>

    var draftOn=true;

    var draftClass=document.getElementsByClassName(document.getElementById("main"), "span", "draft");

    function hideShowDraft(){

    if(draftOn==true){

    draftClass.style.display="none";

    draftOn=false;

    } else if(draftOn==false){

    draftClass.style.display="inline";

    draftOn=true;

    }

    }

    </code>

    I'm trying to make a function where I can hide and show text that's put in <code><span class="draft"></code>. I have a text anchor <code><a onclick="hideShowDraft();">Hide/Show<a></code> that when clicked changes the CSS of all the <code>span</code> tags with the class <code>"draft"</code>.

    I'm doing this in Blogger, so I don't know if it's a template problem or what. I put the script tag <code><script language="text/javscript" src="blah/blah/getElementsByClassName.js"> </script></code> in the <code>head</code> tag, and I know that I linked the script right because it's in the same folder as my other scripts. Spelling's right and everything.

    I know I wrote the function right because I tested it by making the background image to none: <code>document.body.style.backgroundImage="none";</code> when I click on it. I just can't seem to get <code>getElementsByClassName</code> to work. I couldn't get <code>getElementsByTagName</code> to work either when I was just targeting the <code>span</code> tag. Can anyone figure out what the problem is? Thank you so much!

  • Robert Nyman says:

    Ivan,

    First, you need to find all the elements with the class draft class after the HTML elements are in place. This can, for example, be done by inserting your code after all the HTML or by using a window.onload event. Plese read more about this in AJAX, JavaScript and accessibility.

    Then, in the function hideShowDraft you need to loop through all the elements in the variable draftClass, since it is an array of elements. Like this:

    function hideShowDraft(){
    for(var i=0; i<draftClass.length; i++){
    if(draftOn==true){
    draftClass[i].style.display=”none”;
    draftOn=false;

    } else if(draftOn==false){
    draftClass[i].style.display=”inline”;
    draftOn=true;
    }
    }
    }

  • Ivan says:

    Thank you so much, Robert! I really appreciate you helping me out. Keep up the good work!

  • Robert Nyman says:

    Ivan,

    No problem Good luck!

  • engtech says:

    I was using this one for Firefox-specific greasemonkey scripts. I just did some profiling, and I should have used XPATH. :)

    <code>

    function $x(p, context) {

    if (!context) context = document;

    var i, arr = [], xpr = document.evaluate(p, context, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);

    for (i = 0; item = xpr.snapshotItem(i); i++) arr.push(item);

    return arr;

    }

    $x("//a[contains(@class,'current')]");

    </code>

  • Chris Neale says:

    Should downloadable file include both old and revised versions of the function(s) ?

  • Robert Nyman says:

    engtech,

    Yes, for Firefox, XPath is much faster.

    Chris,

    Yes, it contains all version. Then choose the one you seem fit and implement it where you want to use it.

  • Coder says:

    Thank you, ths is by far the best explanation and script i have found around, this saved me so much time.

    Thanks again …

  • […] world who have already noticed this, so there are many already written. I use Robert Nyman’s getElementsByClassName […]

  • Shannon Fong says:

    THANK YOU!!!!

  • Robert Nyman says:

    Shannon,

    You're welcome. :-)

  • […] kaut gan kas t?ds pras?s ?oti bieži. Tikai daž?di Javascript veid?gi risin?jumi k? The Ultimate getElementsByClassName vai ar? daž?die freimworki, kas pied?v? l?dz?gu […]

  • Crash says:

    Wired. This Function comes pretty bloated. Multiple classNames, callback functions and tagName parameter. So much work for a simple function? Native getElementsByClassName don't have those parameters.

    I've wrote my own speeed version above, to just to have one (be cause some ppl ask for). I write JavaScript all day, I know my DOM and never had a use for such a function.

  • Robert Nyman says:

    Crash,

    So, you have never had any use for such a method? Interesting. How do you implement your solutions in an accessible and unobtrusive manner? To me, there's no better way to select out any number of elements where <code>id</code> won't be sufficient, rather than by their class name.

    Multiple class names is in a separate version and not needed. Not sure what you mean with a callback function. <code>tagName</code> is there to optimize performance, something which is not needed in the same way when it is implemented natively.

  • Lisa Milne says:

    Here's my own version of a Javascript getElementsByClassName, it adds the function prototype to the Object class, giving it the same functionality as getElementById and getElementsByTagName

    Usage:

    document.getElementsByClassName('myClass'); // returns array of all elements of class 'myClass'

    or

    document.getElementById('myId').getElementsByClassName('myClass'); // returns array of all elements of class 'myClass' who's parentNode has the id 'myId'

    or even

    document.getElementsByClassName('myClass')[0].getElementsByClassName('anotherClass'); // returns array of all elements of class 'anotherClass' who's parentNode is the first element in the document of class 'myClass'

    Object.prototype.getElementsByClassName = function(clsName) {

      var retVal = new Array();

      var elements = this.getElementsByTagName('*');

      for (var i=0; i= 0) {

          var classes = elements[i].className.split(' ');

          for (var j=0; j<classes.length; j++) {

            if (classes[j] == clsName)

              retVal.push(elements[i]);

          }

        }else if (elements[i].className == clsName)

          retVal.push(elements[i]);

      }

      return retVal;

    }

  • Robert Nyman says:

    Lisa,

    Yes, I've seen that approach before, but i prefer having just one loop and using regular expressions, instead of crating new arrays all the time with split.

    Also, it is never good practice to attach something to the core Object in JavaScript, so I'd recommend adding it to the HTML elements themselves in the web page.

  • […] that the above JS code depends on Robert Nyman’s ultimate getElementsByClassName implementation and the Prototype JavaScript framework. filed in Internet/technology () tagged with ie7, […]

  • Jovica Aleksik says:

    Hi.

    I didn't like the way this function had to be called, I found it too un-intuitive. Mostly when I need a function like this, I need it short and simple. Also, I didn't like to pass an object to the function for all the classes I needed – too complicated, I wanted to have it as flexible as in html's class="" itself.

    Now I don't know whether it is bad for the overall performance when you add functions to the Node prototype itself, but that's what I just did: Now you can call the function on any node to get elements of given classnames inside of it. Of course you can use it on the document node itself too, to get all matching elements in the whole documnt.

    Maybe this is a good place to get some profound answers to the performance question – Does one have to be careful with Node-prototype-functions regarding the performance of the site/document??

    However, here are some examples of how to call the version changed by me:

    <code>

    var e = document.getElementsByClassName('green')[0];

    var form = document.getElementsByClassName('floating login box', 'form');

    var form = document.getElementsByClassName('login box floating', 'form');

    /* if you need only elements within a specific element: */

    var container = document.getElementById('container');

    var boxes = container.getElementsByClassName('box');

    /* handy: */

    var boxes = $('container').getElementsByClassName('box');

    </code>

    So here is the changed version:

    <code>Node.prototype.getElementsByClassName = function () {

    /*

    Written by Jonathan Snook, http://www.snook.ca/jonathan
    Add-ons by Robert Nyman, http://www.robertnyman.com
    and Jovica Aleksik, loopmode@loopmode.de

    arguments: className, [tagName]

    */

    if (this.nodeType != 9) {

    return;

    }

    var oElm = this;

    var oClassNames = arguments[0].indexOf(' ') != -1 ? arguments[0].split(' ') : arguments[0];

    var strTagName = arguments[1] ? arguments[1] : '*';

    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<oClassNames.length; 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<arrElements.length; j++){

    oElement = arrElements[j];

    bMatchesAll = true;

    for(var k=0; k<arrRegExpClassNames.length; k++){

    if(!arrRegExpClassNames[k].test(oElement.className)){

    bMatchesAll = false;

    break;

    }

    }

    if(bMatchesAll){

    arrReturnElements.push(oElement);

    }

    }

    return (arrReturnElements)

    };

    </code>

  • Jovica Aleksik says:

    Shame on me, didn't even look at previous comments…and missed object-prototype version above :( :D Quite similar, though I allowed for space-separated, multiple classnames in addition.

    Heh.. :)

    So what about performance issues – no matter at all?

    Possible/hypothetical differences between object and Node prototype?

  • Robert Nyman says:

    Jovica,

    I have no information about performance, but it seems a bit superfluous to add the method to every element in the web page. When it comes to calling the method on an element itself, though, I agree that it is a nicer approach.

    My suggestion is to only add the method to elements that need it, and it's the base approach of my DOMAssistant library.

  • […] un JavaScript busca todos esos spans (el c?digo para seleccionar los spans de una cierta clase ni siquiera es m?o) y le a?ade el punteado, la mosca y el enlace. Con la gracia que si dese?ramos cambiar de buscador, […]

  • […] Usually JavaScript libraries include a getElementsByClassName function. However, if you’re building your own personal library, you may want to include your own version. Robert Nyman has built and amended over the past 2.5 years a function for this very method: The Ultimate getElementsByClassName! […]

  • […] I was able to quickly find Robert Nyman’s elegant getElementsByClassName function in The Ultimate GetElementsByClassName. […]

  • […] make this bookmarklet I used some code from this site, and this little tool helped me out to compress the code. Posted inProjects, SoftwareApril 7th, […]

  • […] as well. I would keep it in your arsenal if I were you. Furthermore, I am also borrowing the getElementsByClassName script from Robert Nyman. This will allow us to apply our javascript dynamically to our clock […]

  • […] The Ultimate getElementsByClassName – A simple JS function to get elements by classname. Needed it to get my bookmarklet to work. […]

  • Very good and usefull, thanks

  • Seb says:

    I couldn't get this working at first – turns out the code you have on this page has superfluos slashes in the regular expression. Copied and pasted from the JS file instead and that version was fine.

  • Robert Nyman says:

    website design,

    Glad you like it!

    Seb,

    Ah, it was due to trying to prevent some WordPress escaping. The code in the post is ok now too.

  • […] 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, […]

  • […] based Component So I implemented a very simple Sys.Component JavaScript object that uses the getElementsByClassName function to pull out all of the input elements with the watermark CSS class applied to them. The […]

  • […] to them. To fetch these elements I am using a helper function called getElementsByClassName (taken from here) that scans the panel to find all of the elements that have this maintain-scroll class applied to […]

  • […] Robert Nyman publicó en 2005 un artículo en el que mostraba la “Ultimate getElementsByClassName“, una función desarrollada en Javascript que sin necesidad de usar ningún tipo de framework nos permite obtener todos los elementos de una misma clase pasada por parámetro. […]

  • Simo says:

    The ultimate solution is to use mootools =) it's offer a similar solution =)

  • […] Nyman, no ano de 2005, publicou em seu blog um artigo entitulado “The Ultimate getElementsByClassName“, uma função desenvolvida em JavaScript, que sem a utilização de […]

  • […] world who have already noticed this, so there are many already written. I use Robert Nyman’s getElementsByClassName […]

  • Mark says:

    works awesome, thanks so much

  • Dmitry Dedukhin says:

    It seems that there is a bug in your getElementsByClassName realization in IE6.

    I've got an "ObjectType error" in IE6, after quick investigation I've found that IE 6 supports document.getElementsByClassName but doesn't support elm.getElementsByClassName (where elm!=document).

    So, the fix was very simple, on line 6:

    <code>

    – if (document.getElementsByClassName) {

    + elm = elm || document;

    + if (elm.getElementsByClassName) {

    </code>

  • Robert Nyman says:

    Dmitry,

    Thanks, but IE 6 does not support <code>document.getElementsByClassName</code>. In that case, you have something in your test page causing such a behavior.

  • Dmitry Dedukhin says:

    Hmm, very strange…

    I have no any JS-frameworks on my page which can cause such a behavior in IE6.

    But this issue is reproduced in some Opera versions, particulary in 9.26

    Anyway, thanks for the getElementsByClassName

  • toniL says:

    hi,

    i've wrote an getElementsByClassName too.

    Are there any improvement suggestions?

    you can call it these ways:

    var myObj = document.getElementsByClassName(['foo', 'bar'], 'a');

    var myObj = document.getElementsByClassName(['foo', 'bar']);

    var myObj = document.getElementsByClassName('foo', 'a');

    var myObj = document.getElementsByClassName('foo');

    document.getElementsByClassName = function(cl,elem){

    var elementsByClassName = [];

    var regExpString = '';

    if(typeof(elem) == 'undefined') elem = '*';

    if(typeof(cl) == 'object'){

    var countOfCl = cl.length;

    var OR = "|";

    for(var k=0; k < cl.length; k++){

    countOfCl–;

    if(countOfCl == 0) OR = '';

    regExpString += 'b'+cl[k]+'b'+OR;

    }

    }else{

    regExpString = 'b'+cl+'b';

    }

    var regExpString = new RegExp(regExpString);

    var elements = this.getElementsByTagName(elem);

    for (var i = 0; i < elements.length; i++) {

    var className = elements[i].className;

    if (regExpString.test(className)) elementsByClassName.push(elements[i]);

    }

    return elementsByClassName;

    };

    Greetings from Berlin

    toni

  • Robert Nyman says:

    toniL,

    My initial feedback would be that you shouldn't overwrite the native <code>document.getElementsByClassName</code> in web browsers if it already exists, but rather utilize it.

  • Tony says:

    I am not sure what I am doing wrong here, but what I am trying to do is to is to detect a browser and version (which I can do) and then switch off and on between the two spans.

    var myBrowserVersion;

    myBrowserVersion = parseInt(BrowserDetect.version);

    //Detect if visitor is using Firefox 2 (less that version 3)

    //If so just use a mailto method to send email.

    //Otherwise use the abuse form

    if (BrowserDetect.browser == "Firefox"){

    //BrowserDetect.browser == "Firefox" && myBrowserVersion < 3

    var badBrowser = getElementsByClassName("bad-Browser");

    for(i = 0; i < badBrowser.length; i++) {

    badBrowser.style.display = "none";

    goodBrowser.style.display = "block";

    }

    }else {

    var goodBrowser = getElementsByClassName("good-Browser");

    for(i = 0; i < goodBrowser.length; i++) {

    badBrowser.style.display = "block";

    goodBrowser.style.display = "none";

    }

    }

    <a>Report abuse

    <a href="showdiv('comment${comment.commentID}Menu');" rel="nofollow">Report Abuse

  • Tony says:

    The editor cut this part off (I think)

    <a>Report abuse

    <a href="showdiv('comment${comment.commentID}Menu');" rel="nofollow">Report Abuse

  • Tony says:

    < span class="abuse bad-Browser" style="display: none;"> onething

    < span class="abuse good-Browser" style="display: block;"> another thing

    This is part of the span code I want to alternately show/ hide.

    Sorry about not paying attention to the HTML instructions

  • Robert Nyman says:

    Tony,

    Start by using the new version, linked to at the top of this page. If it fails, please provide a URL to a test page.

  • Heinz Stapff says:

    Looking at the getelementsbyclassname again it looks like it is adding a class name to the id attribute. I don't know if that means I don't have to add the class= to the html or not? I have the 'a' tag writen as…

    id="removeitem" class="removeitem"????????????

  • Heinz Stapff says:

    Sorry I can't add/attach events to 'a' link in Table Row Table 'cell' to delete the row onclick with getelementbyclassname in IE. My guess is that IE dose not allow the class refrence to affect the table. Without the ability to do so I remain dumbfounded. I also assume that because the ultimate can't access the 'a' in an IE table row 'cell', it falls short of ultimate and unobtrusive javascript has a while to go??????

  • Robert Nyman says:

    Heinz,

    This function does not add anything to an element, it's just a way to select them.

    About your problems with IE: my guess is that you have called the function through <code>document.getElementsByClassName</code>, and not <code>getElemenetsByClassName</code>, so you don't get any references back to the element.

    If you have a URL to a sample, please add it here.

  • Yngve says:

    Great function!

    Thanks.

    (Made by a Swede, naturally ;) ;) )

  • Heinz Stapff says:

    THANKS ROBERT I'LL TRY IT AGAIN TODAY. I'M ALSO GUESSING THAT OEL

    IS CLASHING WITH EL IN MY ADDEVENT HANDLER. I'M HAVING A HARD TIME WITH POSTING SCRIPT WITH & LT FOR ETC.

  • Heinz Stapff says:

    SORRY I GET THE BUTTON ELEMENTS WITH GETELEMENTS BY CLASS

    USING ***VAR BUTTON=GET*** WITHOUT DOCUMENT?

    THEN USE THE ADD EVENT TO ADD THE EVENT TO THE BUTTON. ONLY THING IS THAT THE GET USES OEL FOR ELEMENT? AND THE ADD USES EL?

  • Robert Nyman says:

    Heinz,

    I really can't say why the rest of your code doesn't work, especially not when I can't see it. This function works to select elements with a certain class.

    To be able to even have a chance to say what's wrong, you need to post a URL to where I can see your code in action.

  • Heinz Stapff says:

    I JUST TRIED POSTING THE SCRIPT AGAIN USING

    SCRIPT

    BUT I DON'T SEE IT POSTED. MY SITE IS STILL IN MY DOCUMENTS FOLDER.

  • Heinz Stapff says:

    << for

    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.

  • Heinz Stapff says:

    HAY I USED & L T AT THE BEGINING OF EVERY LINE OF SCRIPT AND & G T AT THE END OF EVERY LINE BUT IT DOESN'T LOOK LIKE THE POST MADE IT.

  • Heinz Stapff says:

    THE ONE THAT DID ONLY SHOWS HALF? I PUT & L T ; AT THE BEGINING OF THE SCRIPT AND & G T ; AT THE END?

  • Robert Nyman says:

    Heinz,

    You need to escape the script properly to post it in a comment (although a public URL where it can be viewwd is much more preferable).

    Either way, you should really be using a later version of the script.

  • ralph says:

    Nice method! Does anyone know how to get elements with classname1 OR classname2. I know I could use the third parameter as "["classname1", "classname2"]". But that includes only elements with both classnames. I would like to include elements that have or both or one of the specefied names.

  • Robert Nyman says:

    ralph,

    It's an interesting approach, but not supported by this function. I guess your best bet is one of the major JavaScript libraries, and then combine CSS selectors and check if one of them finds any matches.

  • Martin says:

    With all the references out there about document.getElementsByClassName, I thought today's browsers supported it but was obviously wrong. Seemed like such an obvious function to have.

    Copied your function – worked a treat. Many thanks! :)

  • Robert Nyman says:

    Martin,

    Thanks, glad you liked it!

  • does says:

    my mistake. ofcourse tje document needs to be loaded first. shame on me.

  • Robert Nyman says:

    does,

    Glad it works!

  • kiash says:

    Nice,
    Thanks! I think it will be helpful to my project.

  • luke says:

    why won’t this work? getElementsByClassName(document.getElementById(“left-nav”), “a”, “selected”).style.background=’#dee';

    thanks

  • Robert Nyman says:

    luke,

    Since the function returns an array of matching elements. If you only want to do something with the first match, you need to change it to this:

    getElementsByClassName(document.getElementById(“left-nav”), “a”, “selected”)[0].style.background=’#dee’;

    If you want to change all elements, you need to loop over all the matches, and change them one by one.

  • Carney says:

    I’m doing a thing of the identical interest and are taking note with this .Thanks.

  • John Schock says:

    I am very grateful for this code. I had to resort to using getElementsByClassName as a work-around for an inability to load individual elements into an object array onload. Since my entire script depended on getElementsByClassName to compensate for this, I slammed my head through my desk when I found out it didn’t work in Internet Explorer 8. It’s near the end of 2011 and Microsoft still hasn’t figured out the basics of API, it’s time they just retire their project and leave it to the people who actually care about making progress in technology.

  • Robert Nyman says:

    John Schock,

    Glad it was of use for you!

  • […] known Jonathan Snook for many years, since the early days of my blogging, where we co-operated on a getElementsByClassName function. We’ve met in a number of places in the world, and now I’m happy to have […]

  • Hugo Puyol says:

    Had some problems making work the last version, so I tried May 11th version, which worked awesome at once.

    This function save me a lot of code writing time, developing a data table
    with cells having a generic class.

    Thanks!

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>