Monday code giveaway: getElementsByAttribute

Updated September 27th 2006

Apparently Opera’s claim to support document.all in conjunction with not mimicking it exactly like IE led to some problems in Opera 9. Thanks to Ash Searle who tipped me about this and also explained what the problem was. The code below and the JavaScript file to download are updated.

Since we all have to face a new hard tough week now, I thought I’d brighten your day by giving you some code that might be useful.

Ever run into a situation where you want to get an array of all elements with a specific attribute? Or even want elements with a certain value for that chosen attribute, as well? That’s not a problem anymore; let me present getElementsByAttribute.


/*
	Copyright Robert Nyman, http://www.robertnyman.com
	Free to use if this text is included
*/
function getElementsByAttribute(oElm, strTagName, strAttributeName, strAttributeValue){
	var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
	var arrReturnElements = new Array();
	var oAttributeValue = (typeof strAttributeValue != "undefined")? new RegExp("(^|\\s)" + strAttributeValue + "(\\s|$)") : null;
	var oCurrent;
	var oAttribute;
	for(var i=0; i<arrElements.length; i++){
		oCurrent = arrElements[i];
		oAttribute = oCurrent.getAttribute && oCurrent.getAttribute(strAttributeName);
		if(typeof oAttribute == "string" && oAttribute.length > 0){
			if(typeof strAttributeValue == "undefined" || (oAttributeValue && oAttributeValue.test(oAttribute))){
				arrReturnElements.push(oCurrent);
			}
		}
	}
	return arrReturnElements;
}

The parameters are:

oElm
Mandatory. This is element in whose children you will look for the attribute.
strTagName
Mandatory. This is the name of the HTML elements you want to look in. Use wildcard (*) if you want to look in all elements.
strAttributeName
Mandatory. The name of the attribute you’re looking for.
strAttributeValue
Optional. If you want the attribute you’re looking for to have a certain value as well.

And these are a couple of examples how it can be called:


getElementsByAttribute(document.body, "*", "id");
getElementsByAttribute(document.getElementById("the-form"), "input", "type", "text");

Web browser compatibility

The code has been tested and verified to work in:

  • All Firefox versions, all the way back to Firebird (remember that one? :-))
  • IE 5.0+, PC
  • IE 5.2, Mac
  • Safari 1.3
  • Opera 7.03

To use it, just copy and paste above code or download the getElementsByAttribute.js file.

46 Comments

  • Ok, as someone knowing the basics of DOM scripting, I think this looks really sexy and I can think of many uses for it.

    Thanks for sharing. It's nicely tucked away in my bookmarks now. 🙂

  • Nice! This will really come in handy. I have used Jonathan Snook's getElementsByClassName before but I think it's time for a change. Thanks for sharing.

  • Marco says:

    Very nice! This will surely come in handy at times just like the getElementsByClassName thing.

  • Alexander says:

    Thank you, Robert!

  • Jules says:

    You know I need to know more JavaScript when I read that you were giving away a function called getElementsByAttitude.

    So, which elements have attitude?

  • Robert Nyman says:

    Jeroen,

    Thanks!

    Emil,

    Thank you! Regarding <code>getElementsByClassName</code>, I wrote an improved version, inspired by Snook's initial one.

    Marco, Alexander,

    Thanks!

    Jules,

    Ha ha! 🙂

    Well, it just has to be the <code>strong</code> element, right? 🙂

  • Chris says:

    Great! Just yesterday I needed this function. Thanks Robert.

  • Robert Nyman says:

    Chris,

    Great, I'm glad I could help!

  • Mozilla has this native for <code>Document</code> objects implementing the <code>XULDocument</code> interface.

  • Robert Nyman says:

    Anne,

    Ah, interesting to hear!

  • Rowan Lewis says:

    Nice spotting Anne!

    Robert, thanks for sharing, functions like this should always have been part of the spec…

  • Robert Nyman says:

    Rowan,

    Yes, this one and <code>getElementsByClassName</code> are good examples of things that should be native.

  • Hakan Bilgin says:

    A little to much coding for my taste. What do you think about this one?

    // Javascript

    function getChildren(el, a, v) {

        var ar = new Array();

        var ac = document.getElementById(el).getElementsByTagName('*');

        for (c=0; c<ac.length; c++) if (v? (ac[c][a] == v) : (ac[c][a])) ar.push(ac[c]);

        return ar;

    }

    // Usage

    getChildren(elementId, attribute, value);

    // Sample

    var col1 = getChildren('my_element', 'className', 'menuitem');

    var col2 = getChildren('my_element', 'nodeName', 'IMG');

    var col3 = getChildren('my_element', 'onclick');

    The third parameter (value) is optional…if undefined the function will return any element with specified attributename, regardless value.

    /hbi

  • Robert Nyman says:

    Hakan,

    Very short and concise, which I like! However, there are a few problems with it.

    <code>getElementsByTagName(’*')</code> only works on the <code>document</code> element in IE 5, and not on any other element; hence the usage of <code>document.all</code>.

    Just checking for <code>v?</code> will return false in that if clause if you would indeed want to look for an attribute with the value <code>0</code>.

    Referring to directly to an attribute's name through an array (<code>(ac[c][a] == v)</code>) didn't seem to work in Safari, and it threw an unexplainable error in IE on some elements. Another problem with this value comparison is that it's case-sensitive, while some web browser are notorious for changing the case when the elements and attributes are created in their DOM.

    Finally, the reason for checking the length of the attribute value is because IE automatically creates a lot of attributes for every element in the DOM at runtime, so I want to make sure that is an attribute created intentionally by the programmer.

  • I am trying to use your code and keep getting errors that an object is required. Front Page even tells me that the HTML is no good. At this point, I suppose that is possible. Here is the button code I am trying to use to get it to run. What I am trying to do is find a div id tag that equals a certain value and then pass it to another function that I kinow works. I am trying to provide buttons on a page to copy specific code. The code I use works when I only have one code copying area per page. If I have two, then it will not work. So I figured that fi I could use code to find a specific one and then copy only that one, it might work. In any event, here is the button code that I use:

    Eval –>

    Thanks,

    Gene

  • Robert Nyman says:

    EuGene,

    I think you didn't escape the HTML code properly when you posted your comment (&lt; for < etc) </code>. To get a reference to an element with a certain <code>id</code>, just use <code>document.getElementById("element-id")</code> (remember that <code>id</code>s are supposed to be unique, meaning that only one instance of a certain <code>id</code> is correct).

    If that doesn't solve your problems, I think there might be two otherreasons why it doesn't work.

    1) The first parameter has to be an object reference to the container where you want to look for your objects. Make sure it's valid.

    2) You will not get back an object but an array reference from the function. I.e., if you think you will only get back one object, you need to reference it like this:

    <code>var oObject = getElementsByAttribute(document.body, "*", "id")[0];</code>

    Good luck!

  • Hi, it's a nice snippet.

    However, you might consider alternative for the <code>push()</code> which is not supported by IE 5.0/Win.

  • Robert Nyman says:

    Marko,

    That's the reason why there's also an implementation for the <code>push</code> method for the <code>Array</code> object in the downloadable JavaScript file.

    However, maybe I should've pointed this out in the post as well…

  • Great implementation of a general method! 🙂

    And now for my wish: Could it be possibel to add a NAMESPACE parametre? You know if I add my own namespace – say "CSL" and have an attribute set on an INPUT tag: <INPUT type="TEXT" CSL:KeepForHours="1">

    Your method wil not be able to find elements based on attributename "CSL:KeepForHours" will it?

    Why would I use namespace?

    – I am working on an easy way to add cookie support for any tag through simpel HTML mark-up and a Javascript Library.

    The Attribute I am considering to put in it's own namespace. An example way to ensure that a value will live across sessions would be like the input tag named above. In that example the value of the input tag would live for 1 hour by using cookies 🙂

    Cheers!

    /Sten

    Denmark

  • Robert Nyman says:

    Sten,

    Thank you!

    What you can do is just tweak the function to use the getAttributeNS method.

  • […] bsp;  Firefox 1.0+, Mozilla ?, Opera ? * getElementsByAttribute     * getElementsByAttribute (parent, tag name, attribute name, attribute […]

  • bob says:

    third time I've come back for this after losing it – about time I said thanks.

    thanks.

  • jim says:

    Hi,

    I haven't done extensive testing on this, but I seem to have stumbled across a problem with custom attributes.

    I have following element:

    <code>

    <input type="button" foo="bar" value="initValue"/>

    </code>

    If I call your function as follows:

    <code>

    var buttons = getElementsByAttribute(document.body, 'input', 'foo', 'bar');

    </code>

    I get null result in Firefox 1.5.0.4 (it's ok on IE6).

    By changing your line:

    <code>

    oAttribute = oCurrent.getAttribute(strAttributeName);

    </code>

    to instead use eval() to do direct property access:

    <code>

    oAttribute = eval('oCurrent.'+strAttributeName);

    </code>

    it seems to work ok on both Firefox and IE6. I'm assuming there is no intentional restriction in getAttribute function with regards to custom attributes, and that this is just a bug in Firefox Element::getAttribute.

    Thanks for your function!

  • Robert Nyman says:

    Jim,

    I didn't know that. My guess is that it is probably intentional by Firefox since they want to be as valid and correct as possible, and custom attributes shouldn't be in the HTML code (this is a big discussion for another day… :-)) and aren't allowed if you want to have a web page with valid code (unless you use a custom DTD and so on).

    Anyway, with that said, if you have that specific need for custom attributes I'm happy to hear that your workaround seems to work out fine for you. However, I will let this function hold on to the W3C recommended way of accessing attributes in the DOM.

  • Hakan Bilgin says:

    Hi Robert,

    I am currently experimenting with "custom" namespaces and have discovered that the interface in Firefox is a little unsatisfactory. For instance in IE, with the properties scopeName and nodeName, I can find out contextual information about a certain element.

    In Firefox, scopeName does not exist at all (but I have extended as seen below) and nodeName returns both the namespace and the nodeName, united by a colon…in upperCase.

    Node.prototype.__defineGetter__('scopeName', function() {

    var nn = this.nodeName.toString();

    return (nn.indexOf(':') > -1)? nn.split(':')[0].toLowerCase() : 'HTML' ;

    });

    I find the Firefox interface poor and limiting. But than again, I might be missing out on something…so I hope you shed some light to the subject. Do you know any way to extract the properties of an element in a suitable way in Firefox?

    BR,

    /hbi

  • Robert Nyman says:

    Hakan,

    Unfortunately, I don't have an answer for you. I never get time nor assigned to play around with such fun stuff! 🙂

  • Hakan Bilgin says:

    Ok…thanx anyway.

    /hbi

  • Simon says:

    Works great in firefox and opera but not at all in IE6 for my implemention.

    But as it's the class I wanted I found your getElementsByClassName which worked fine in all 3 browsers.

  • Robert Nyman says:

    Simon,

    Weird… I use it all the time and haven't ahd any problems with IE 6. For example, with my GLT library it's vital.

  • Matthew Krivanek says:

    What I'm trying to do is find all instances of inline styles and remove them.

    However, it seems that the attribute 'style' does not get returned in IE. I've tested in other browsers and the 'style' attribute is found, but IE, the array is returned empty.

    Any thoughts on how to make this work?

  • Matthew Krivanek says:

    It's seems I may have found a solution, however, not so graceful.

    Due to the way IE works, when trying to get the 'style' attribute, IE's getAttribute function returns an object.

    The work around I found was to set the oAttribute variable using the .cssText property for IE. I believe that the 'style' attribute is the only attribute stored as an object in IE. I could be wrong though…

    My modification to your function:

    <code>

    /*

    Copyright Robert Nyman, http://www.robertnyman.com
    Free to use if this text is included

    */

    function getElementsByAttribute(oElm, strTagName, strAttributeName, strAttributeValue){

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

    var arrReturnElements = new Array();

    var oAttributeValue = (typeof strAttributeValue != "undefined")? new RegExp("(^|s)" + strAttributeValue + "(s|$)") : null;

    var oCurrent;

    var oAttribute;

    for(var i=0; iif(typeof oAttribute == "object"){

    oAttribute = oAttribute.cssText;

    }

    if(typeof oAttribute == "string" && oAttribute.length > 0){

    if(typeof strAttributeValue == "undefined" || (oAttributeValue && oAttributeValue.test(oAttribute))){

    arrReturnElements.push(oCurrent);

    }

    }

    }

    return arrReturnElements;

    }

    </code>

  • Ok, so, my code broke Firefox. This is dirty, but works.

    <code>if (navigator.appName == 'Microsoft Internet Explorer')

    {

    oAttribute = oAttribute.cssText;

    }

    </code>

  • Robert Nyman says:

    Matthew,

    Interesting. However, the way I see it, <code>style</code> isn't really an attribute in that sense. You never set a style through <code>setAttribute</code>, but instead by using the <code>.style</code> notation.

    If your code works in your case, I'm happy for you, but I don't find it necessary to incorporate in the standard version of the script.

    A little suggestion: Instead of checking for web browser name, you can use <code>if(document.all)</code> and then a check for <code>if(typeof oAttribute.cssText != "undefined)</code>.

  • […] d, if you want to, a certain value in that attribute as well. More thoroughly described in Monday code giveaway: getElementsByAttribute. preventDefau […]

  • Thanks for the tip and thanks again for the script!

    -mk

  • Robert Nyman says:

    Matthew,

    No problem at all! I hope everything works out for you.

  • Gary says:

    Okay, so I'm wondering if someone could kindly help me with this a bit. Long story why, but in essence I want to find the ID of a single div element among many which have been dynamically generated in javascript (each element is assigned an ID along the lines of cell_1_2, cell_1_3, etc). That element has a unique style (a background color) and is the only element belonging to a specific class (called "highlighted").

    I'm not sure if this is the right function, or if getElementByClass is better, but in either case I'm having a hard time returning the ID of that single element.

    Any thoughts? Much appreciated …

  • Kok Chuan says:

    Hi, this code works great. However i've found something that causes it not to work properly.

    when i create a "input" form control without specifying the type, it defaults to type="text". your current script is not able to work with this "lazy" coding 😀

  • […] Monday code giveaway: getElementsByAttribute – Robert’s talk – Web development and Internet tr… […]

  • […] fuera de un framework como jQuery o Mootools. Actualmente existen unas cuantas escritas como la de Robert Nyman y la incluida en el diccionario XUL de desarrollo de extensiones para […]

  • paul says:

    Robet u did a grat job, really a very helpful example indeed

  • John says:

    Great code, however it seems to stumble with ’empty’ attributes, such as HTML5’s ‘autofocus’ … unless you employ the unnecessary ‘autofocus=”autofocus”‘.

    code: getElementsByAttribute(document.body,’*’,’autofocus’,’?’)

    I’ve tried ‘true’, ‘*’ and even ” for the fourth (‘?’) argument to the function, with out joy. Any clues? (This in Google Chrome; have not tried other browsers).

  • Robert Nyman says:

    John,

    That might be true. I haven’t touched this code in over four years, so feel free to do necessary alterations.

  • Guilherme says:

    A beauty way to comment

    * @param oElm:
    * Mandatory. This is element in whose children you will look for the attribute.
    * @param strTagName:
    * Mandatory. This is the name of the HTML elements you want to look in. Use wildcard (*) if you want to look in all elements.
    * @param strAttributeName:
    * Mandatory. The name of the attribute you’re looking for.
    * @param strAttributeValue:
    * Optional. If you want the attribute you’re looking for to have a certain value as well.

Leave a Reply

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