15

Per,

How to iterate through all attributes in an HTML element?

you get the general solution:

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

How would I make this general solution more specific to only alert the values of the data- attribute.

Would I need to regex attrib.name or is there a simpler way? Here is some sample HTML with 2 data- attributes:

<div id='universals' data-path='/zz/' data-load='1'></div>
Community
  • 1
  • 1

5 Answers5

28

dataset support is very good if you don't need IE below version 11

A simple "for-in" iteration on the dataset property:

var dataset = document.querySelector('div').dataset;

for( var d in dataset)
   console.log(d, dataset[d])
<div data-foo='1' data-bar='2'></div>
Community
  • 1
  • 1
vsync
  • 118,978
  • 58
  • 307
  • 400
7

In many modern browsers we have access to these special attributes via the .dataset member on the Node object. Unfortunately, this is not yet an accepted standard, and as such we don't see this being adopted all across the spectrum. Fortunately, there is partial support in every major browser in that these attributes can still be accessed using common methods like getAttribute, as well as by cycling over the .attributes list.

The code below shows the second method:

// Reference to our element
var element = document.getElementById("universals"), attr;

// Cycle over each attribute on the element
for (var i = 0; i < element.attributes.length; i++) {
    // Store reference to current attr
    attr = element.attributes[i];
    // If attribute nodeName starts with 'data-'
    if (/^data-/.test(attr.nodeName)) {
        // Log its name (minus the 'data-' part), and its value
        console.log(
            "Key: " + attr.nodeName.replace(/^data-/, ''),
            "Val: " + attr.nodeValue
        );
    }
}

Fiddle: http://jsfiddle.net/pGGqf/14/

You should find that this approach will work in every major browser, even as far back as IE6. This isn't necessary, again, in browsers that support the .dataset member. There's a bit of extra functionality offered on the .dataset object, so you are free to feature-detect it if you like:

if (element.dataset) {
    // Browser supports dataset member
} else {
    // Browser does not support dataset member
}
Sampson
  • 265,109
  • 74
  • 539
  • 565
  • @pure_code Did you need a solution that has backwards compat? The `.dataset` member won't be too broadly supported across the board. – Sampson Dec 26 '12 at 21:35
  • @pure_code Compat data can be found [here](https://developer.mozilla.org/en-US/docs/DOM/element.dataset). Let me expand my answer to include this information, as well as alternative approaches. – Sampson Dec 26 '12 at 21:41
  • @pure_code It means you can use those attributes, and access them with `getAttribute`. – Sampson Dec 26 '12 at 21:50
  • @pure_code I have provided a feature-detection approach that works in IE10. – Sampson Dec 26 '12 at 21:56
  • @pure_code IE is a pretty awesome browser (especially of late). Just note that unless something is standardized, we shouldn't expect it across the board. For instance, Microsoft's Pointer Model is superior to anything else the competition offers, but is not yet provided in Chrome, or Firefox. – Sampson Dec 26 '12 at 22:01
  • Your dataset fallback code contains several errors. The attribute must be prefixed with `data-` which is removed from the name as seen in the dataset, and any further `-` characters are removed but cause the subsequent character to appear as upper case in the dataset. Firefox lowercases characters not following `-` but I think that's non-standard. (Also a true dataset would be read and write live, but that's way off scope here.) – Neil Dec 26 '12 at 22:03
  • at this point a regex or startswith (if it is supported )would be simpler. –  Dec 26 '12 at 22:05
  • @pure_code I've updated my answer to contain a rather simple approach that will give you support back as far as IE6. You will run into compat problems using `.startswith` as it was added in ES6. – Sampson Dec 26 '12 at 23:36
  • thanks again, I just started testing in IE10 and it looks like I iwll indeed have to loop through the attributes. –  Feb 15 '13 at 22:58
6
_dataToObject = function( dataset ) {
    return Object.keys( dataset ).reduce( function( object, key ) {
        object[ key ] = dataset[ key ]; 
        return object;
    }, {});
}
user1767210
  • 603
  • 6
  • 5
1

If you don't want to use regex you could try

if attrib.name.startswith('data'):
    //do something
Sam Sharp
  • 359
  • 1
  • 7
  • Does startswith work with major modern browsers? - (IE10+, FF, Safari, Chrome ) –  Dec 26 '12 at 22:14
  • https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/startsWith –  Dec 26 '12 at 22:14
-1

I have a concept here that may work for you.

var el = document.getElementById("universals");
for(var i=0;i<el.attributes.length;i++){
    if((el.attributes[i].nodeName+"").indexOf("data-")>-1){
        var key=(el.attributes[i].nodeName+"").substring(5);
        var value=el.attributes[i].value;
        alert(key+": "+value);
    }
}

This is the basic idea I have come up with that seems to work pretty well. I have also created function which returns the a Name-Value-Pair object out of an HTMLElements "data" attributes using this method above.

function data2Obj(id){
  var obj={};
  var el=document.getElementById(id);
  for(var i=0;i<el.attributes.length;i++){
    if((el.attributes[i].nodeName+"").indexOf("data-")>-1){
      var key=(el.attributes[i].nodeName+"").substring(5);
      var value=el.attributes[i].value;
      if(value.toLowerCase()=="true")value=true;
      else if(value.toLowerCase()=="false")value=false;
      else if((parseInt(value)+"")==value)value=parseInt(value);
      obj[key]=value;
    }
  }
  return obj;
}

This can easily be modified to accept a class or any other method for selecting HTMLElements.

If you take the return of this function and iterate through the object you should get your desired result.

var datas = data2Obj("universal");
for(var k in datas){
  alert(k+": "+datas[k]);
}
Dustin Poissant
  • 3,201
  • 1
  • 20
  • 32