Put that data-* attribute away, son...You might hurt someone

HTML 5 data-* attributes allow us to add custom attributes to elements as long as they are prefixed with ‘data-’ and since this was first discussed on John Resig’s blog I’ve been interested in how people will use and abuse this feature. I greeted the feature with mixed feelings. It’s definitely a simple way to enrich the semantic value of HTML pages as well as helping to improve some of the more toxic parts of Microformats. XML namespaces are definitely a more complete solution but this is a simple and immeadiately adoptable means to add invisible semantic data to HTML documents.

However, as John hinted in his post, there’s an enormous temptation for JavaScript authors to use this to embed configuration data for their scripts directly into HTML. Many developers have been itching for an excuse to do this for a long time. Some just added attributes willy-nilly like crazy web standards bandits, some would love to do add arbiturary configuration into their HTML but felt a bit squeamish about moving away from the HTML specs and opted to abuse the class attribute from within the standard. For the record, I’d tend to side with the former. If it works and there’s a good reason for it then I say do it. However, back then I explained why there is no good reason to add unsemantic configuration data into your HTML and now that we have standards-approved carte-blanche to do this I’d like to reiterate that it’s still not the way forward. If you’ve not read that article then its worth a quick read before you go on.

By all means, use data-* attributes to add semantically valuable data to your HTML but if you are just using it to prop up a script you are writing think again.

An Example

If you’ve not already watched it go now and watch Yehuda’s Screencast on evented programming with jQuery. The ideas in here represent a massive progression in client-side scripting. It’s nothing short of essential viewing. However, it also happens to be the latest example I’ve come across of needless use of data-* attributes and, while not wanting to take away from how progressive and clever the content is as a whole, I feel the need to use it as my counter-example for this article.

In the screencast, Yehuda is creating a tab interface. The markup he proposes is something like this:

<ul class="tabs">
  <li data-content="first">First</li>
  <li data-content="second">Second</li>
  <li data-content="third">Third</li>
</ul>

<div class="pane" id="first">Some content</div>
<div class="pane" id="second">Some content</div>
<div class="pane" id="third" class="selected">Some content</div>

The idea being that when the tab <li> is clicked the script then interrogates data-content to decide which div to show. However, without the JavaScript operating on this the HTML has no semantical value. The <li>s are just list elements (and will be read as such by assistive technologies). In fact, the browser doesn’t know that those list elements are in anyway associated with <div>s below. Here’s how I think it should be marked up:

<ul class="section-nav">
  <li><a href="#first">First</a></li>
  <li><a href="#second">Second</a></li>
  <li><a href="#third">Third</a></li>
</ul>

<div class="section" id="first">Some content</div>
<div class="section" id="second">Some content</div>
<div class="section" id="third">Some content</div>

Now, before we even add JavaScript we have links that we can click that will jump you to the specified content. If you click the back button you will jump back to the previous tab’s content. With this in place you could even make tabs work solely by using CSS and the :target pseudo-selector. If you wanted to go HTML 5 crazy you could even use <nav> and <section> elements which would further enhance the semantics of the document. By correctly associating the tab link and the tab content we can take advantage of the browsers facilities to navigate this type of content even before we get out the old JavaScript crowbar.

With this markup as a base it’s then just as trivial to hook in the script but instead of interogatting data-content we just look at the anchor of the link. Because we are now using anchors, users can deep link into a particular tab, it would be trivial to support the back button and assistive technologies will make better sense of it, amoung other things.

Leave Yehuda Alone!

Of course, I’m picking apart what was a very simple and purposefully contrived example, but as usage of data-* attributes picks up, it’s important to not abuse this facility and to continue find as many semantic hooks for your scripts as possible. It may now be a “standard” but it doesn’t mean that its a good solution. When looking for hooks for my scripts, this is the process I follow:

1. Build up your markup to be as meaningful as possible. If it submits a request it should be a <form>, if its linking to another piece of content it’s an <a>. Even if you’re building a very complex piece of UI seek to build as much of it as you can into your document (while keeping the semantics intact) before you go anywhere near your JavaScript.

2. Write your script to take advantage of the semantics your HTML document has to offer. This will get you a long way in many cases, however, you may well find that there is still configuration information you need to pass into your script. Rather than turn to data-* attributes its best to consider inferring this information via context in the same way that CSS does. This way you can assert things like “all <input>s with type ‘slider’ and a class ‘day’ have a min of 1 and a max of 31” then you can change this in one place rather than visiting each element’s data-* attributes individually. Read this article for more detail on how to do that. We don’t need to change the heading colour in every single heading element in our site now we have CSS, let’s not start doing that kind of thing again now we have data-* attributes.

I welcome the data-* attribute. It’s a simple and immediately useful method to add custom semantic data to HTML documents. Just avoid using it to litter implementation-specific crap into your documents :)

Low Pro for Prototype and Firefox 3.5

Just a quick note for those googling for a solution to this. Older versions of Low Pro for Protoype will be experiencing problems with behaviors in Firefox 3.5. This issue has been fixed so pick up the latest version from GitHub

An interesting upshot of this is that I found out something pretty nice which is kind of obvious but had never occurred to me before. If you create a function in a closure then that function can refer to itself because it has access to that closure’s variables. I normally use arguments.callee to get a reference to a function from within its body but there’s never really any need to do that:

(function() {
  var aFunction = function() {
    aFunction.thing = 47;
  };

  aFunction();

  alert(aFunction.thing); //=> 47;
})();

Massive Robot launches twaud.io

Well, it’s been a bloody long while again since I’ve posted here and I suppose you’ve all unsubscribed me now. To be honest, I wouldn’t blame you.

However, silence here does not mean that I’ve been sitting on my arse doing nothing. In fact, the reality is very much to the contrary. Loads of stuff has been going on but there are two new pieces of news. Firstly, I’ve renamed and rebranded my company. We are now Massive Robot and we are available for consultancy and development work so if you have anything in mind then please contact us. Joe has done the branding and it’s something I’m really pleased with.

Check the business cards…

Massive Robot Business Cards

Along with those is a new site and a T-shirt. The final and most important Massive Robot branding will be the kicks. I’m working on those at the moment.

twaud.io

This week launched twaud.io, Massive Robot’s new service. The idea is simple – it’s like twitpic but for audio. Go to the site or use the API to upload your audio and you get a page with a short URL and a player for your followers to listen to it. This is just the start and we are working on a whole load of new features for it so follow @twaudio to keep up to date.

The build of twaud.io has been an interesting and fun experience. I was able to use Twitter’s new OAuth based login process via twitter_auth which I’d recommend wholeheartedly for as a foundation for Twitter applications, although I have hacked to allow for both OAuth (for the site) and username/password based (for the API) authentication where normally you must choose one or the other. It also uses S3’s direct uploading facility via SWFUpload which was not easy to get going but has provided a really scalable system as the application stays completely out of the loop of the bulky upload process. I love the direction web development is moving in at the moment.

It's Been A Long Time...

But I’ve not just been sitting on my arse playing GTA IV, oh no. Well, not all the time anyway. The reason I’ve not posted anything (or been particularly active on the web in general) is that I’ve been damn busy. Most importantly, Catherine kindly gave birth to our first son, Max, back in March which has been quite a change and sapped a lot of my hacking time. I have to say though, despite the horror stories that many veteran parents like to feed you, our experience has only been good. In fact, not good, great. I recommend this reproducing lark.

Secondly, I’ve been hacking away nearly full time on one of my favourite projects to date, Peoples Music Store with LRUG stalwart and renowned anarchist, James ‘Bringing London To Its Very Knees’ Darling which is maturing nicely under private beta as we speak. Peoples Music Store is a great idea from some of the guys behind bleep.com whereby users can construct and customise their very own download store from the music they love then get free music themselves if people buy from their store. It’s a great way to both promote and show off you’re own music taste or in depth genre knowledge and find new music from stores you trust while getting some free digital swag along the way. I’m probably not explaining it well so just drop me a line if you want and invite and the site will explain itself. Public launch is coming in a month or so.

Building Peoples Music Store has been a great learning experience. We run the site on a cloud computing platform and from content ingestion to audio preview delivery to application servers to download packaging and delivery everything has been designed to scale horizontally – and I’m pretty proud of it. Thin, Rack, Sphinx, God, Starling and a whole load more cool open source gear is all running in there. I really need to get to blogging some of what I’ve discovered about working with Rack. It simply is the dog’s bollocks.

So, enough of the excuses. What’s on the horizon?

Speaking and Conferences

I’ve taken some time of speaking and conferencing in general so as to spend lots of time with Catherine and Max but come September I’m restarting the conference trail. Firstly, I’m doing a presentation and a tutorial (with Jarkko Laine) at RailsConf Europe all about JavaScript related Rails stuff and I’m likely to have a slot at @media Ajax as well. Also, I’ll be heading to dConstruct as is the tradition.

Hacking and Open Source Business

Although I’ve not commited to Low Pro or Low Pro JQ for a good while now they are both very much alive. I’ve simply not come across anything that I’ve felt the need to add for a while. If you have any suggestions or patches do let me know. I’ve actually got time to commit them at the moment. Another little project that I’m hoping to get off the ground is called Evil which is going to contain lots of Merb/Rack goodness. The first by-product of which is the merb_openid gem for consuming OpenID in Merb apps (it’s still not quite production ready though so don’t go using it just yet). I’ll let you know what Evil actually does when (or if) I actually get something working.

So, that’s all for now. Just a bit of a status report. I promise I’ll get some useful content written that you actually care about very soon.

Event Delegation Made Easy

I’m having a lot of fun poking around jQuery at the moment and came up with a cool little thing that’s going into Low Pro for jQuery but is a nice stand-alone little snippet for implementing event delegation. Since the Christian and the guys at YUI started talking about it event delegation has gone from being something that I’d use occasionally to the way I do nearly all my event handling. If you aren’t familiar with the technique go and click that previous link and read Christian’s article now – it’s important.

In most instances I end up writing a lot of event handlers that look like this:

$('#thing').click(function(e) {
  var target = $(e.target);

  if (target.hasClass('quit') return doQuitStuff();
  if (target.hasClass('edit') return doEditStuff();
  // and so on...
});

Obviously, writing a lot of the same kind of code is a warning sign that something needs refactoring but I’ve never come up with a nice way to abstract this. But with a little bit of functional magic I’ve just found with something I really like. Here’s what I came up with:

jQuery.delegate = function(rules) {
  return function(e) {
    var target = $(e.target);
    for (var selector in rules)
      if (target.is(selector)) return rules[selector].apply(this, $.makeArray(arguments));
  }
}

Using it is simple:

$('#thing').click($.delegate({
  '.quit': function() { /* do quit stuff */ },
  '.edit': function() { /* do edit stuff */ }
}));

The function simple runs through the rules checking if the element that fired the event belongs to that selector then calls the corresponding handler passing the original event object through. The great thing about it is that you can use it in Low Pro behavior classes:

DateSelector = $.klass({
  onclick: $.delegate({
    '.close': function() { this.close() },
   '.day': function(e) { this.selectDate(e.target) }
  }),
  selectDate: function(dayElement) {
    // code ...
  },
  close: function() {
    // code ...
  }
});

I’m not sure of the performance implications of using is() so heavily but some form of caching could be added if it was a problem. Still, it’s a really nice little bit of syntactic sugar that’s going into Low Pro for jQuery and I’ll be using it a lot.

UPDATE: I should have added that there’s a version of this in Low Pro for Prototype. In case you want to use it on its own:

Event.delegate = function(rules) {
  return function(e) {
      var element = $(e.element());
      for (var selector in rules)
        if (element.match(selector)) return rules[selector].apply(this, $A(arguments));
    }
}

Meanwhile, you might want to take a look at the patch by Peter Michaux.

Who's This Bloke?

Dan Webb is a London-based web developer specialising in web application development and hording vinyl. danwebb.net is his personal site.

Expect to read about modern DOM Scripting, Ruby on Rails and general web technology. Expect to hear Hip Hop and Funk.

Contact:

Follow me: @danwrong

Hire me: Massive Robot

Site feed: RSS Feed

More about Dan and the site →

From The Code Dump

More code at Github

Recent Linkage del.icio.us del.icio.us RSS feed