59

I need the JavaScript code to iterate through the filled attributes in an HTML element.

This Element.attributes ref says I can access it via index, but does not specify whether it is well supported and can be used (cross-browser).

Or any other ways? (without using any frameworks, like jQuery / Prototype)

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Robin Rodricks
  • 110,798
  • 141
  • 398
  • 607

6 Answers6

59

This would work in IE, Firefox and Chrome (can somebody test the others please? — Thanks, @Bryan):

for (var i = 0; i < elem.attributes.length; i++) {
    var attrib = elem.attributes[i];
    console.log(attrib.name + " = " + attrib.value);
}

EDIT: IE iterates all attributes the DOM object in question supports, no matter whether they have actually been defined in HTML or not.

You must look at the attrib.specified Boolean property to find out if the attribute actually exists. Firefox and Chrome seem to support this property as well:

for (var i = 0; i < elem.attributes.length; i++) {
    var attrib = elem.attributes[i];
    if (attrib.specified) {
        console.log(attrib.name + " = " + attrib.value);
    }
}
Steel Brain
  • 4,321
  • 28
  • 38
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • 3
    Works in Opera and Safari too. – Bryan May 06 '09 at 07:02
  • [This example isn't working for me](http://jsfiddle.net/acLEZ/). It appears that `elem` isn't defined. What am I doing wrong here? – Anderson Green Jun 09 '13 at 02:19
  • 3
    @AndersonGreen If you think about it for a moment it will come to you. – Tomalak Jun 09 '13 at 06:41
  • I tried using `var elem = document.getElementById("Something");`, but [that didn't work either, as seen here](http://jsfiddle.net/acLEZ/1/). – Anderson Green Jun 09 '13 at 07:27
  • @AndersonGreen This sample alerts `id = Something` for me, which is exactly what's supposed to happen. Did you expect something else? – Tomalak Jun 09 '13 at 08:20
  • @Tomalak I thought it was supposed to recursively iterate through the attributes of all the sub-elements of the first element, since that's how I interpreted the question. – Anderson Green Jun 09 '13 at 20:51
  • 1
    @AndersonGreen The word "recursively" does not apear in the question. Interation does not imply recursion. You'll have to implement recursion yourself. – Tomalak Jun 09 '13 at 20:58
  • You should avoid calling `attributes` so many times; cache it in a local variable! – Marc-André Lafortune Mar 15 '15 at 17:44
  • @Marc-AndréLafortune Premature optimization. DOM interactions like this are blazingly fast. (Yes, I know caching makes them this faster, but this bit will *very, very likely* not be the bottleneck of the page.) – Tomalak Mar 15 '15 at 17:50
  • @Tomalak: Let's agree to disagree. Your algorithm is in `O(n^2)`, where `n` is the number of attributes. If you don't change the `O()` of an algorithm, that's fine, but writing something in `O(n)` instead of `O(n^2)` is good practise, not premature, IMO. – Marc-André Lafortune Mar 15 '15 at 21:55
  • 2
    I'm not sure if a for loop is complex enough to call it an algorithm. I am also not quite sure where you get the notion that this would take *O(n²)* time. `elem.attributes.length` is very probably *O(1)*, `elem.attributes[i]` is definitely *O(1)* - so as I see it the whole thing is *O(n)*. But even if I am wrong: *It does not matter*. If you call this for every element on your page you're doing it wrong anyway. Hell, even then it takes only 8ms for this page here on my rusty laptop. And that's with jQuery `$.each()`. In short: I couldn't care less about the runtime characteristics of this bit. – Tomalak Mar 16 '15 at 02:49
19

Another method is to convert the attribute collection to an array using Array.from:

Array.from(element.attributes).forEach(attr => {
  console.log(`${attr.nodeName}=${attr.nodeValue}`);
})
Josh Johnson
  • 10,729
  • 12
  • 60
  • 83
Lloyd
  • 8,204
  • 2
  • 38
  • 53
6

In case anyone is interested in a filtered version, or is trying to build CSS attribute selectors, here you go:

let el = document.body;
Array.from(el.attributes)
    .filter(a => { return a.specified && a.nodeName !== 'class'; })
    .map(a => { return '[' + a.nodeName + '=' + a.textContent + ']'; })
    .join('');

//outputs: "[name=value][name=value]

You can certainly remove the join to retreive an array or add a filter for "style" since in most web applications the style tag is widely manipulated by widgets.

N-ate
  • 6,051
  • 2
  • 40
  • 48
1

More efficient

Array.prototype.forEach.call(elm.attributes, attr => console.log(attr))
Danny Raufeisen
  • 953
  • 8
  • 21
1

The most simple approach is to use spread operator.

const el = document.querySelector('div');

[...el.attributes].forEach((attr) => {
  console.log(attr.name + ' = ' + attr.value);
});
<div class="foo" id="bar"></div>
Jens Törnell
  • 23,180
  • 45
  • 124
  • 206
0

This is quite an old question, but reacting to @N-ate answer, here is a version following the same functional programming approach and that returns a JS Object which keys are the attribute names, and which values are the attributes' associated values:

Array.from(element.attributes)
    .filter(a => a.specified)
    .map(a => ({[a.nodeName]: a.nodeValue}))
    .reduce((prev, curr) => Object.assign(prev || {}, curr))

This turns:

<div class="thingy verse" id="my-div" style="color: red;"><p>Hello</p></div>

Into

{
    class: "thingy verse",
    id: "my-div",
    style: "color: red;"
}
Samuel Prevost
  • 1,047
  • 1
  • 11
  • 30