The Black Magic of HTML5 Data Attributes

Okay, so, rewind to about a year and a half ago, when I was lying on the sofa, having a whine about my retail job du jour, and remembered that I actually had some skills outside of smiling and accepting the blame for my store being out of arbitrarily-sized t-shirts. This train of thought ended up with a friend of mine sending me copies of a couple front-end ebooks to get me up to speed on HTML5 (which had just come out) and CSS3, since I was a little out of date with what was going on in the Great Big Hello World of Web Development.

So, I’m reading this HTML5 book, all like:

But, there was one thing that it either didn’t cover, or—more likely—I got bored and quit reading before I got that part, because it wasn’t like I couldn’t already HTML, and reading about HTML is not the most exciting thing on Earth. (Also, HTML is now a verb, I’ve decided.) The thing that I missed? Data attributes, or, as they are also known, black freakin’ magic. The first time I saw them being used, I was like:

…look, kids, back in the day, we had to come up with weird names for CSS classes and use JavaScript to parse them into something meaningful if we wanted data available in the DOM. Now, you just prefix with “data-*” on an element, and you can store whatever the heck kind of string you want in there (within reason). You can store JSON in it, for example, which is just—whaaaaaat, where has this been all my life? (Or maybe not—HTML5 is only two years old, so for the vast majority of my life it hasn’t existed… still, have I totally been living under a rock for two years?!)

So now, say I have an unordered list, and I want to grab a value (or several values) and display it when the user selects a list item, without making an Ajax call or querying a database or any of that nonsense. I’ll just stick my data in some data attributes—let’s call them “data-registration-number”, “data-color”, and “data-codename”:

  • <ul id="nonsenseList">
    
     <li data-registration-number="1701" data-color="yellow"
    
     data-codename="Enterprise">Nonsense</li> 
    
     <li data-registration-number="74656" data-color="blue"
    
     data-codename="Voyager">More Nonsense</li>
    
     <li data-registration-number="74205" data-color="red"
    
     data-codename="Defiant">Even More Nonsense</li>
    
     </ul>

So now, I can use JavaScript to grab one of those values from my list items, and use it however I want:

<script> 
//this first script uses the element.getAttribute() function to nab the value from our data attribute:
var nonsenseList = document.getElementById("nonsenseList"); 
var firstItem = nonsenseList.children[0]; 
firstItem.onmouseover = function(){ 
 var regNumber = firstItem.getAttribute("data-registration-number"); 
 window.alert("Registration Number: " + regNumber); 
}; 
</script>

Admittedly, this is a pretty lame example with little/no practical application. The point is, I was able to tell my script to get the value of the attribute named “data-number” off a particular element, and it did. You should note that in order to have to a valid data attribute, the attribute name must be prefixed with “data-”, have at least one character after the first hyphen, be XML-compatible, and contain no uppercase ASCII letters (as per w3 spec for HTML documents, read more here).

But wait! There’s more. There is actually an even better way to access what is stored in your data attributes, and it’s through the new HTML5 dataset API. This API exposes an element attribute named dataset, which returns a DOMStringMap object. The DOMStringMap keys are the names of the data attributes (without the “data-” prefix), and the corresponding values are the string values contained in the attributes themselves. If the data attribute name is made of multiple words (like our data-registration-number), it’s converted to camelCase (in this case, registrationNumber). The dataset API is dead simple to use, too—just call it using dot notation on a chosen element:

<script>
//let's try it again using the dataset API:
var nonsenseList = document.getElementById("nonsenseList"); 
var secondItem = nonsenseList.children[1]; 
secondItem.onmouseover = function(){
 var regNumber = secondItem.dataset.registrationNumber; 
 window.alert("Registration Number: " + regNumber); 
}; 
</script>

The dataset API is widely supported in modern desktop and mobile browsers (aside from in Internet Explorer, which only supports it in IE11, because it’s Internet Explorer). There is a shim out there for it (only a Google search away), but if you’re concerned about compatibility, you can always fall back on using more vanilla Javascript functions like getAttribute(). I’ve also read that using the dataset API is slower, but unless you’ve got a load of stuff stored in your attributes or need to read out of a whole bunch of them very quickly, performance shouldn’t be a concern—and, side note, if this is an issue, it’s my opinion that you’re probably weighing down the DOM with too much data, and need to rethink your architecture.

Another side note: data attributes should only be used when a suitable HTML5 element or attribute does not already exist. Additionally, unlike microformats, data attributes are meant to be private—that is, they are not intended to be read by outside software, and are meant only for custom data in the context of the site they are located on. Data attributes are meant to be used by the site’s own scripts, meaning that you shouldn’t be implementing them to store publicly-usable metadata. If you want to do that, use microformats, that’s what they’re for!

If you’d like to try it with some quick and dirty code, here’s the pointlessness that I threw together for this post–feel free to pop it into your editor of choice and use it as a starting point for playing around with data attributes:

<html> 
<head>
 <!--doing some quick/dirty styling so that things aren't as ugly-->
 <link href="https://fonts.googleapis.com/css?family=Homemade+Apple" rel="stylesheet"> 
 <style>
 .container { 
 width: 25%; 
 text-align: center;
 }
 li { 
 display: block; 
 margin: 10%; 
 padding: 10%;
 border: 5px solid black; 
 border-radius: 10px;
 font-family: 'Homemade Apple', cursive;
 }
 </style>
</head>
<body>
 <div class="container">
 <ul id="nonsenseList">
 <li data-registration-number="1701" data-color="yellow"
 data-codename="Enterprise">Nonsense</li> 
 <li data-registration-number="74656" data-color="blue"
 data-codename="Voyager">More Nonsense</li>
 <li data-registration-number="74205" data-color="red"
 data-codename="Defiant">Even More Nonsense</li>
 </ul>
 </div>
</body>
</html>

<script> 
//this first script uses the element.getAttribute() function to nab the value from our data attribute:
var nonsenseList = document.getElementById("nonsenseList"); 
var firstItem = nonsenseList.children[0]; 
firstItem.onmouseover = function(){ 
 var regNumber = firstItem.getAttribute("data-registration-number"); 
 window.alert("Registration Number: " + regNumber); 
}; 
</script>

<script>
//let's try it again using the dataset API:
var nonsenseList = document.getElementById("nonsenseList"); 
var secondItem = nonsenseList.children[1]; 
secondItem.onmouseover = function(){
 var regNumber = secondItem.dataset.registrationNumber; 
 window.alert("Registration Number: " + regNumber); 
}; 
</script>