169

Given an arbitrary HTML element with zero or more data-* attributes, how can one retrieve a list of key-value pairs for the data.

E.g. given this:

<div id='prod' data-id='10' data-cat='toy' data-cid='42'>blah</div>

I would like to be able to programmatically retrieve this:

{ "id":10, "cat":"toy", "cid":42 }

Using jQuery (v1.4.3), accessing the individual bits of data using $.data() is simple if the keys are known in advance, but it is not obvious how one can do so with arbitrary sets of data.

I'm looking for a 'simple' jQuery solution if one exists, but would not mind a lower level approach otherwise. I had a go at trying to to parse $('#prod').attributes but my lack of javascript-fu is letting me down.

update

customdata does what I need. However, including a jQuery plugin just for a fraction of its functionality seemed like an overkill.

Eyeballing the source helped me fix my own code (and improved my javascript-fu).

Here's the solution I came up with:

function getDataAttributes(node) {
    var d = {}, 
        re_dataAttr = /^data\-(.+)$/;

    $.each(node.get(0).attributes, function(index, attr) {
        if (re_dataAttr.test(attr.nodeName)) {
            var key = attr.nodeName.match(re_dataAttr)[1];
            d[key] = attr.nodeValue;
        }
    });

    return d;
}

update 2

As demonstrated in the accepted answer, the solution is trivial with jQuery (>=1.4.4). $('#prod').data() would return the required data dict.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Shawn Chin
  • 84,080
  • 19
  • 162
  • 191
  • Addresing "update 2" be aware of jquery data() use some internal cache and data(key, value) does not propagate to DOM, which can cause lot of headaches. Also since jquery 3 data() convert attribute data-some-thing="test" into {someThing: "test"} – ETNyx Jul 11 '19 at 13:18

11 Answers11

104

Actually, if you're working with jQuery, as of version 1.4.3 1.4.4 (because of the bug as mentioned in the comments below), data-* attributes are supported through .data():

As of jQuery 1.4.3 HTML 5 data- attributes will be automatically pulled in to jQuery's data object.

Note that strings are left intact while JavaScript values are converted to their associated value (this includes booleans, numbers, objects, arrays, and null). The data- attributes are pulled in the first time the data property is accessed and then are no longer accessed or mutated (all data values are then stored internally in jQuery).

The jQuery.fn.data function will return all of the data- attribute inside an object as key-value pairs, with the key being the part of the attribute name after data- and the value being the value of that attribute after being converted following the rules stated above.

I've also created a simple demo if that doesn't convince you: http://jsfiddle.net/yijiang/WVfSg/

osullic
  • 543
  • 1
  • 8
  • 21
Yi Jiang
  • 49,435
  • 16
  • 136
  • 136
  • 3
    No, this is [broken in 1.4.3](http://bugs.jquery.com/ticket/7222). A minimum of 1.4.4 is explicitly required. – Crescent Fresh Nov 16 '10 at 05:17
  • Thanks. That's exactly what I was looking for. I previously tried that with 1.4.3 and didn't go very far. jsfiddle demo helped too. – Shawn Chin Nov 16 '10 at 09:02
  • 2
    @Isc: one thing that is very annoying is jQuery's attempt at "deserializing" the value found in the attribute (i.e. [from master](https://github.com/jquery/jquery/commit/8a5df39045292397a06d08b4fab2ad29819b5d44/): `!jQuery.isNaN( data ) ? parseFloat( data ) : ...`). I had product serial numbers in my attribute such as `data-serial="00071134"` which jQuery munged into a number `71134`, forcing me to revert to the less elegant `.attr('data-serial')` (not an option in your case). I've seen questions on SO that have voiced similar frustrations wrt `.data()`, will try to dig one out... – Crescent Fresh Nov 16 '10 at 15:32
  • @CrescentFresh: Good point. Definitely something I should watch out for. In my solution (https://gist.github.com/701652) I fall back to parsing node.attributes when jQuery<1.4.3 ; with this issue in mind, perhaps I should simply stick with manually parsing the attributes. – Shawn Chin Nov 16 '10 at 18:10
  • @Lsc: yes your <= 1.4.3 implementation does not match jQuery's as far as the "smart" parsing jQuery tries to do. I would have actually preferred if jQuery's implementation matched the simplicity of yours :) – Crescent Fresh Nov 16 '10 at 19:43
  • 8
    Keep in mind that `$.data()` also returns anything you've stored using `$.data(key, value)`. If you truly want to check if something is actually stored in the markup as a data-* attribute, this method might not be the best choice. – Philip Walton Feb 16 '12 at 22:33
  • I was also burned by `$.data()`'s [camel-casing](http://stackoverflow.com/questions/22753629/jquery-get-html-5-data-attributes-with-hyphens-and-case-sensitivity) behavior. – Turgar Nov 06 '15 at 14:42
  • how can i add these attributes to a variable to be used on another element? – user7201898 Sep 08 '21 at 16:09
90

A pure JavaScript solution ought to be offered as well, as the solution is not difficult:

var a = [].filter.call(el.attributes, function(at) { return /^data-/.test(at.name); });

This gives an array of attribute objects, which have name and value properties:

if (a.length) {
    var firstAttributeName = a[0].name;
    var firstAttributeValue = a[0].value;
}

Edit: To take it a step further, you can get a dictionary by iterating the attributes and populating a data object:

var data = {};
[].forEach.call(el.attributes, function(attr) {
    if (/^data-/.test(attr.name)) {
        var camelCaseName = attr.name.substr(5).replace(/-(.)/g, function ($0, $1) {
            return $1.toUpperCase();
        });
        data[camelCaseName] = attr.value;
    }
});

You could then access the value of, for example, data-my-value="2" as data.myValue;

jsfiddle.net/3KFYf/33

Edit: If you wanted to set data attributes on your element programmatically from an object, you could:

Object.keys(data).forEach(function(key) {
    var attrName = "data-" + key.replace(/[A-Z]/g, function($0) {
        return "-" + $0.toLowerCase();
    });
    el.setAttribute(attrName, data[key]);
});

jsfiddle.net/3KFYf/34

EDIT: If you are using babel or TypeScript, or coding only for es6 browsers, this is a nice place to use es6 arrow functions, and shorten the code a bit:

var a = [].filter.call(el.attributes, at => /^data-/.test(at.name));
gilly3
  • 87,962
  • 25
  • 144
  • 176
  • Good answer! The first line does not replace the `-` character like jQuery does but the edited answer does. – Timo Huovinen Apr 03 '14 at 12:43
  • @gilly3 Your jsfiddle doesn't seem to work. Can you have a look? – Ethan Feb 20 '15 at 09:56
  • 1
    @Ethan - Fixed. Thanks for letting me know. I had been using beautify.js from [jsbeautifier.org](http://jsbeautifier.org) to pretty print the JSON. Apparently that link is broken now. But, that was overkill since `JSON.stringify()` has JSON formatting built in. – gilly3 Feb 20 '15 at 17:40
  • @gilly3 Fantastic, your routine is great to fetch all. Would it be easy to add fetch data for a given key as well as to update modified data for key(s)? If you can share, that would be great. – Ethan Feb 20 '15 at 21:52
  • @Ethan - For getting a single value, don't overthink it: `el.getAttribute("data-foo")`. I've updated my answer to show how you could set data attributes based on an object. – gilly3 Feb 20 '15 at 22:28
  • @gilly3 Thanks for the code. Yes, I shouldn't overthink to get that done, but however, having similar data within the code makes it more readable. However, I should be able to write it up quickly. – Ethan Feb 22 '15 at 20:46
  • Contains all the questions / answers i was looking for! – Pimmol Jan 31 '17 at 10:22
27

Have a look here:

If the browser also supports the HTML5 JavaScript API, you should be able to get the data with:

var attributes = element.dataset

or

var cat = element.dataset.cat

Oh, but I also read:

Unfortunately, the new dataset property has not yet been implemented in any browser, so in the meantime it’s best to use getAttribute and setAttribute as demonstrated earlier.

It is from May 2010.


If you use jQuery anyway, you might want to have a look at the customdata plugin. I have no experience with it though.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • Also of interest, from ppk: http://www.quirksmode.org/dom/w3c_core.html#attributes – Pointy Nov 15 '10 at 17:27
  • Thanks Felix. I had come across customdata. Unfortunately, it does not do what I need -- i.e. get all data given that keys are not known in advance. – Shawn Chin Nov 15 '10 at 17:30
  • @lsc: The documentation says that `$("#my").customdata()` should give you `{method: "delete", remote: "true"}`... so it should work. – Felix Kling Nov 15 '10 at 17:39
  • Using a separate jQuery plugin seems like an overkill, but looking at the source for customdata helped my fix my own solutions! Will accept your answer and update Q with working function. – Shawn Chin Nov 15 '10 at 17:47
  • @Felix: Appreciate your answer, but Yi Jiang's a closer match to what I was looking for, hence moved "accepted" to his. Many thanks. – Shawn Chin Nov 16 '10 at 09:04
  • 1
    @lsc: Totally fine, build in solutions should always be preferred imo. Unfortunately I'm not up-to-date with the latest jQuery development ;) Just wondering why someone voted me down... – Felix Kling Nov 16 '10 at 09:12
11

As mentioned above modern browsers have the The HTMLElement.dataset API.
That API gives you a DOMStringMap, and you can retrieve the list of data-* attributes simply doing:

var dataset = el.dataset; // as you asked in the question

you can also retrieve a array with the data- property's key names like

var data = Object.keys(el.dataset);

or map its values by

Object.keys(el.dataset).map(function(key){ return el.dataset[key];});
// or the ES6 way: Object.keys(el.dataset).map(key=>{ return el.dataset[key];});

and like this you can iterate those and use them without the need of filtering between all attributes of the element like we needed to do before.

Community
  • 1
  • 1
Sergio
  • 28,539
  • 11
  • 85
  • 132
3

You should be get the data through the dataset attributes

var data = element.dataset;

dataset is useful tool for get data-attribute

Brad Larson
  • 170,088
  • 45
  • 397
  • 571
lychi
  • 286
  • 4
  • 4
  • IIRC, `this.dataset` does not work on all browsers yet. There is however a [jquery plugin](http://www.orangesoda.net/jquery.dataset.html) which provides this feature. – Shawn Chin Apr 30 '12 at 08:30
3

or convert gilly3's excellent answer to a jQuery method:

$.fn.info = function () {
    var data = {};
    [].forEach.call(this.get(0).attributes, function (attr) {
        if (/^data-/.test(attr.name)) {
            var camelCaseName = attr.name.substr(5).replace(/-(.)/g, function ($0, $1) {
                return $1.toUpperCase();
            });
            data[camelCaseName] = attr.value;
        }
    });
    return data;
}

Using: $('.foo').info();

Handsome Nerd
  • 17,114
  • 22
  • 95
  • 173
2

You can just iterate over the data attributes like any other object to get keys and values, here's how to do it with $.each:

    $.each($('#myEl').data(), function(key, value) {
        console.log(key);
        console.log(value);
    });
Andrew
  • 18,680
  • 13
  • 103
  • 118
2

I use nested each - for me this is the easiest solution (Easy to control/change "what you do with the values - in my example output data-attributes as ul-list) (Jquery Code)

var model = $(".model");

var ul = $("<ul>").appendTo("body");

$(model).each(function(index, item) {
  ul.append($(document.createElement("li")).text($(this).text()));
  $.each($(this).data(), function(key, value) {
    ul.append($(document.createElement("strong")).text(key + ": " + value));
    ul.append($(document.createElement("br")));
  }); //inner each
  ul.append($(document.createElement("hr")));
}); // outer each

/*print html*/
var htmlString = $("ul").html();
$("code").text(htmlString);
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/prism.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/themes/prism-okaidia.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1 id="demo"></h1>

<ul>
  <li class="model" data-price="45$" data-location="Italy" data-id="1234">Model 1</li>
  <li class="model" data-price="75$" data-location="Israel" data-id="4321">Model 2</li> 
  <li class="model" data-price="99$" data-location="France" data-id="1212">Model 3</li> 
</ul>

<pre>
<code class="language-html">
  
</code>
</pre>

<h2>Generate list by code</h2>
<br>

Codepen: https://codepen.io/ezra_siton/pen/GRgRwNw?editors=1111

Ezra Siton
  • 6,887
  • 2
  • 25
  • 37
0

One way of finding all data attributes is using element.attributes. Using .attributes, you can loop through all of the element attributes, filtering out the items which include the string "data-".

let element = document.getElementById("element");

function getDataAttributes(element){
    let elementAttributes = {},
        i = 0;

    while(i < element.attributes.length){
        if(element.attributes[i].name.includes("data-")){
            elementAttributes[element.attributes[i].name] = element.attributes[i].value
        }
        i++;
    }

    return elementAttributes;

}
Jmorel88
  • 66
  • 2
  • 12
0

If you know the name of keys you can also use object destructuring to get values like this

const {id, cat, cid } = document.getElementById('prod').dataset;

You can also skip keys you don't need and get the ones you need like this

const { cid, id } = document.getElementById('prod').dataset;
irfandar
  • 1,690
  • 1
  • 23
  • 24
0

100% Javascript no jQuery ;)

DOMStringMap :

  console.log(document.getElementById('target-element-id').dataset);

or custom variable :

  var data = {};

  Object.entries(document.getElementById('target-element-id').dataset).forEach(([key, val]) => {
    data[key] = val;
  });

  console.log(data);
Gigoland
  • 1,287
  • 13
  • 10