Christian Heilmann

Data attributes rock – as both CSS and JavaScript know them

Wednesday, October 10th, 2012 at 3:06 pm

Currently my better half Kasia is working on a JavaScript training course and wanted to explain the concepts of JavaScript with a game. So we sat down and did a simple game example whilst she was fretting over the structure of the course. As she wanted to explain how to interact with the DOM in JavaScript rather than using Canvas we had some fun using CSS animation in conjunction with simple keyboard controls. More on the game in due time, but here is a quick thing we found to be extremely useful and not really used enough in the wild – the interplay of data attributes, CSS and changing states.

Defining a player element

We wanted to make the game hackable, people playing with HTML could change it. That was more a request by me as Mozilla has the Webmaker project and there will be a lot of game hacking at Mozfest in November.

In order to define a player element the semantic fan in me would do something like this:

<div id="player">
	<ul>
		<li class="name">Joe</li>
		<li class="score">100</li>
	</ul>
</div>

This makes sense in terms of HTML, and is accessible, too. However, in terms of accessing this in JavaScript, it is quite annoying as you need three element matches. Also in terms of maintenance it means three elements. In JS you’d need to do something like:

var player = document.querySelector('#player'),
    name   = document.querySelector('#player .name'),
    score  = document.querySelector('#player .score');

In order to change the score value, you’d need to change the innerHTML of the score reference.

score.innerHTML = 10;

Aside: yes I know there are lots of HTML templating solutions and I am sure dozens of jQuery solutions for that, but let’s stick to vanilla JS as this was about teaching that.

A HTML5 player element

Instead of going through these pains, we found it to be much easier to go with data attributes:

<div id="dataplayer" data-name="Joe" data-score="100">
</div>

The clever thing here is that HTML5 already gives us an API to change this data:

var player = document.querySelector('#dataplayer');
 
// read
alert('Score:' + player.dataset.score);
alert('Name:' + player.dataset.name);
 
// write
player.dataset.score = 10;
 
// read again
alert('Score:' + player.dataset.score);

Re-using attribute values

Another benefit of using data attributes is that CSS gets them. Say for example you want to show the colour of the score value in red when it reaches 10. In the first HTML using a list you’d need to do the testing in JavaScript and add a class to have a different display. You could of course also change the colour directly with the style collection but that is awful in terms of maintenance. It can cause reflows in your rendering and also means another thing to explain to maintainers.

function changescore(newscore) {
	if (newscore === 10) {
		score.classList.add('low');
	} else {
		score.classList.remove('low');
	}
}

#player .low {
	color: #c00;
}

Aside: jQuery has contains(‘foo’) to match elements with the text in their node content in the selector engine, but it has been deprecated as a CSS standard, so that is not the way to go.

When using data attributes you don’t need that – all you need is an attribute selector in CSS:

#dataplayer[data-score='10'] {
	color: #c00;
}

To display the scores you can use generated content in CSS:

#dataplayer::after {
	content: attr(data-name);
	position: absolute; 
	left: -50px;
}
#dataplayer::before {
	opacity: 0;
	content: attr(data-score);
	position: absolute; 
	left: 100px;
}

Check out the following fiddle to see all in action: http://jsfiddle.net/codepo8/BMY6H/

The only downside I can think of is that only Firefox allows for transitions and animations on generated content. All in all we found data attributes incredibly useful though.

Comments? Here are the threads on Google+ or Facebook.

Tags: ,

Share on Mastodon (needs instance)

Share on Twitter

My other work: