The element.dataset API

By  on  

A while back I showed you the awesome classList API, which allows simple addition, removal, and toggling of CSS classes without the need for parsing the className.  Another simple API available in FireFox Aurora Firefox 6, at time of print and Chrome 8 is the element.dataset API.  This tiny API allows developers to get and set data- attribute values on HTML elements.   Let's take a look at how it works!

As you know, you can add information to HTML elements using custom data- attributes.  You can name these data- attributes however you'd like.  There are a few rules for working with the dataset API:

  • element.dataset cannot be referenced directly -- you will receive an error
  • You reference data- property names by camel-casing their attribute names
  • The name cannot start with xml
  • The name cannot contain uppercase letters

Assume the following element is within the page:

<div id="myDiv" data-name="myDiv" data-id="myId" data-my-custom-key="This is the value"></div>

To retrieve the data-id attribute value, you'd code:

// Get the element
var element = document.getElementById("myDiv");

// Get the id
var id = element.dataset.id;

To retrieve the data-my-custom-key attribute, you'd code:

// Retrieves "data-my-custom-key"
var customKey = element.dataset.myCustomKey;

Assigning the value to a custom attribute you would look like:

// Sets the value to something else
element.dataset.myCustomKey = "Some other value";

// Element would be:
//		<div id="myDiv" data-name="myDiv" data-id="myId" data-my-custom-key="Some other value"></div>	

If a data- attribute you programmatically set does not exist, it will be created:

// Set new data- attribute
element.dataset.mootoolsFtw = "true";

// Element would be:
//		<div id="myDiv" data-name="myDiv" data-id="myId" data-my-custom-key="Some other value" data-mootools-ftw="true"></div>

It probably goes without saying but you cannot store objects within element.dataset without serializing them first.  I'm not aware of dataset length restrictions but storing large sets of data via dataset will make for a bloated DOM which would be difficult to debug.  I do think this little nugget is useful though!

Recent Features

  • By
    5 More HTML5 APIs You Didn&#8217;t Know Existed

    The HTML5 revolution has provided us some awesome JavaScript and HTML APIs.  Some are APIs we knew we've needed for years, others are cutting edge mobile and desktop helpers.  Regardless of API strength or purpose, anything to help us better do our job is a...

  • By
    Facebook Open Graph META Tags

    It's no secret that Facebook has become a major traffic driver for all types of websites.  Nowadays even large corporations steer consumers toward their Facebook pages instead of the corporate websites directly.  And of course there are Facebook "Like" and "Recommend" widgets on every website.  One...

Incredible Demos

  • By
    LightFace:  Facebook Lightbox for MooTools

    One of the web components I've always loved has been Facebook's modal dialog.  This "lightbox" isn't like others:  no dark overlay, no obnoxious animating to size, and it doesn't try to do "too much."  With Facebook's dialog in mind, I've created LightFace:  a Facebook lightbox...

  • By
    WebSocket and Socket.IO

    My favorite web technology is quickly becoming the WebSocket API. WebSocket provides a welcomed alternative to the AJAX technologies we've been making use of over the past few years. This new API provides a method to push messages from client to server efficiently...

Discussion

  1. Dutchie

    Yeah, that is useful stuff as long as you’re working with a framework that uses this if available or other ways if not. I think there’s only 1% change you specifically need to target the browser that supports this (atm)…

  2. Thanks for the write-up. I try to avoid this API because it’s certainly not mainstream, but Moo makes it easy to use the data attributes anyways.

    As selectors:

    document.getElements([data-something-or-other]');

    As data:

    element.get('data-something-or-other');

    Now, we could have an interesting discussion about whether declarative markup is unobtrusive, but we should have it another day… :)

  3. Savageman

    Opera 11.10+ also supports this, and the Aurora is currently Firefox 6 (if we read the article in 6 months, we won’t be able to guess what was the Aurora when the article was written).

  4. I use the MooTools element.store and element.retrieve quite often. It’s similar to this, but you can store anything, including objects. I’m sure there is a large overhead, but the ability to store a reference to another element is great.

    Such as:

    $$(".someElement").each(function(element) {
        var elementToggler = element.getParent().getParent().getElement(".someToggler");
        element.store("toggler", elementToggler);
        element.addEvent("mouseenter", function(event) {
            this.retrieve("toggler").addClass("highlight");
        });
        element.addEvent("mouseleave", function(event) {
            this.retrieve("toggler").removeClass("highlight");
        });
    });
    

    You can see that even thought we need a convoluted call to initially get the toggler, once we have the reference we can store it and call it much more easily. You can do that same with things like order numbers, arrays of changed elements, ect.

    You could probably achieve similar functionality by giving the toggler a unique ID and saving it to the element.dataset and calling it back that way, but I think in that particular case, the store/retrieve would have a smaller overhead.

    Actually, looking through the MooTools code, it appears that the store/retrieve function has a very small overhead. It is just storing the data in an object that is referenced to the element. It would actually be really simple to implement on your own.

    var storage = {};
    var get = function(uid){
        return (storage[uid] || (storage[uid] = {}));
    };
    var retrieve = function(element, property, default){
        var storage = get(element.uid), prop = storage[property];
        if (default != null && prop == null) prop = storage[property] = default;
        return prop != null ? prop : null;
    }
    var store = function(element, property, value){
        var storage = get(element.uid);
        storage[property] = value;
        return this;
    }
    

    This method doesn’t take advantage of the Element implementation, obviously, but it is still simple. The great thing about this is that no string parsing is going on to inject data.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!