Custom Attributes And Class Names

At the Rich Web Experience, Alex Russell did a presentation entitled Standards Heresy, In the presentation, which I didn’t attend so forgive me if I’ve gotten the wrong end of the stick, he spoke about how Dojo has been forced to abandon web standards in some cases in order to get the job done. The conversation was furthered by Aaron Gustafson’s response and is a really interesting discussion. However, I’m not going to comment on the politics of the W3C further as I know nothing about that at all. One example in the presentation reminded me of something I’ve wanted to comment on for a while now. Here’s that example:

<div dojoType="dijit.form.HorizontalSlider"
     name="horizontal1"
     onChange="dojo.byId('slider1input').value=arguments[0];"
     value="10"
     maximum="100"
     minimum="0"
     showButtons="false"
     intermediateChanges="true"
     style="width:50%; height: 20px;"
     id="slider1">
  ...
</div>

This is a snippet of code showing how Dojo is going off the standards rails by adding custom attributes that are ‘needed’ to configure a slider widget. This is a really good example of a case where you definitely shouldn’t add custom attributes to HTML. While the W3C/browser vendors/whipping boy of choice might be letting us down what the web standards movement taught us (most importantly in my mind) is that semantic HTML is critically important. Having a <div> with custom attributes that tell Dojo it’s a slider widget is of no use to anything apart from as a crutch to Dojo. It has no semantic value at all. Outside of the context of Dojo it’s just an useless lump of markup.

Although HTML is a pretty limited vocabulary which is very document centric we can still go much further than this in attempting to describe what this is semantically. It’s an input so use an input or a select box as the base element. How browsers and assistive technologies deal with these elements is a lot closer to what we want than a meaningless <div>. Secondly, there’s custom attributes – having attributes containing behavioural information like showButtons and intermediateChanges are equally as semantically useless as the old presentational attributes like bgcolor and border and we’ve all learnt the disadvantages of mixing content and presentation and discarded those…hopefully. There’s got to be a better way.

So we want our semantic HTML and to keep our behaviour data out of our document. Take a leaf out of CSS’s book. We can name and classify objects and use these as our hooks to attach our JavaScript powered behaviour. Eureka! Not quite:

<!-- Created with http://cow.neondragon.net/stuff/reflection/reflectomatic.html  -->
<img src="/reflection/photos/giraffe.jpg" width="132" alt="" class="reflect ropacity71" />

The above code is from a nice little JavaScript effects library called reflection.js which uses class names to hook in it’s behaviour unobtrusively. However, take a closer look at the class names. They aren’t classifying things as such, they are embedding configuration into directly into the class names. In a similar way it’s not a generally thought of as a good idea to give elements class names like ‘bigred’ or ‘width40’ although, depressingly, I’ve seen it done! This is definitely an abuse of class names – its not a ‘bigred’ its a ‘price’ and so on. This is seen not just in reflection.js but a large amount of ‘unobtrusive’ libraries.

So how can we improve on this? We need to provide configuration information to our JavaScript but neither custom attributes or embedding info are looking good. The answer is pretty simple – add another link to the chain and this is were (plug imminent :) projects like behaviour.js and Low Pro come in. They add that other link to the chain. We can say that all inputs with the class ‘date’ should have a date selector attached to them or that selects with the class ‘rating’ should be replaced with a slider that’s range is 0-10 and so on. Here’s a snippet using Low Pro to illustrate this:

Event.addBehavior({
  'select.rating': Slider({ min: 0, max: 10, intermediateValues: true }),
  'input.date': DateSelector
});

In this way we are able to maintain semantic HTML, avoid adding custom attributes and maintain a very loose coupling between our document and it’s behaviour. This, we’ve seen from CSS, is vitally important to maintainability and a real advantage. Via this method you can apply configuration to single items or as a group. If you decided that ‘rating’ selects should in fact all be vertical sliders instead of horizontal then that’s no problem either. It gives you ultimate flexibility.

This methodology is right at the core of Low Pro and I’ve been getting incredible results from it. It’s made what could be really complex JavaScript implementations a breeze. Instead of a huge, fearsome application everything becomes a HTML document with many loosely, coupled smaller components attached to it. If you don’t use Prototype/Low Pro don’t let that stop you from working this way. Bill Burcham wonders whether this can be library agnostic. Well, as an idea, in a sense it already is. All you need is the ability to select by CSS selectors. Dojo and dijit are in fact very well suited.

21 Comments (Closed)

Great post Dan.

One of the areas which literally opened my eyes to meaningful class naming is microformats.

I urge everyone to embrace them!
Once you get a good grasp of the concept, structuring documents becomes a breeze, and working with HTML that mimics your back-end data (or is sometimes even directly mapped to it) is truly refreshing.

The fact that microformats are machine readable ends up just being a nice side effect.

Oh, and when there are no existing microformats for your particular needs: invent them!

Tobie LangelTobie Langel at 08.10.07 / 01AM

Note: Dojo doesn’t need to do anything that way. Dojo (Dijit in this case) provides both a markup approach, and a non-markup approach. In fact, using other areas of Dojo, we can even do something that seems an awful lot like what you were just suggesting.


dojo.query("div.slider").forEach(function(node){
  var widget = new dijit.form.HorizontalSlider(
    {
      value: 10,
      maximum: 100,
      etc
    }, 
    node
  );
  // Manipulate the widget object as needed
});

Dojo is quite flexible, and the same thing can be done in a variety of ways. The argument has always been not that we have to use these odd attributes (I personally never use them), but that adding them doesn’t break the internet, and still allows people who might be frightened by the more advanced approach to adopt these widgets very quickly. I don’t want it to be purity over pragmatism, and I think we would all do well to try to strike a balance rather than pick a side.

NeilNeil at 08.10.07 / 03AM

Neil: Perhaps I should have been more explicit and included an example like your’s but that’s what I was alluding to in the last sentence. Dojo is very well suited to doing things properly although I get the impression from the documentation that doing things the way you do is not the most common way at all. What I was trying to do with this post is explain why it should be the most common way.

I don’t really buy the ‘allows beginners to adopt more quickly’ reasoning. If it’s flawed then why teach people to do it in the first place? And, well, misusing markup like that does break the internet for some people. A div with all those custom attributes is no use to anyone unless Dojo happens to be working but you can’t depend on that all the time. Without JavaScript it’s just junk.

Tobie: Yeah, agreed. It’s a great way of working. Got to be careful not to call the microformats you make up Microformats though. The microformats community don’t like things being refered to as microformats unless they’ve been argued about and put on the official wiki :)

Dan WebbDan Webb at 08.10.07 / 09AM

I wonder about the rel attribute, I’ve been used it for a while, holding json objects thus leaving style to css and using the rel for action.

Ran BaronRan Baron at 08.10.07 / 15PM

rel has semantic value, and that’s:

This attribute describes the relationship from the current document to the anchor specified by the href attribute. The value of this attribute is a space-separated list of link types.

which means it shouldn’t be used to contain JSON. One thing I use rel for, in a quasi-semantic way, is to trigger popup windows using rel="external". Can’t think of any other good examples, but JSON isn’t going to be one of them.

Brad WrightBrad Wright at 08.10.07 / 22PM

I’m guilty of using class names equivalent to width40 and even worse. My excuse is that I don’t create these manually, but rather get them by reflection from Rails model-level validations. (See http://schuerig.de/michael/blog/index.php/2006/12/15/rails-almost-automatic-client-side-validation/)

This is a case where I simply don’t have a nicely abstract name that describes the concept being validated. But what I gain is that the client-side and server-side behavior is consistent by construction.

Michael SchuerigMichael Schuerig at 08.10.07 / 22PM

Ran: Yeah, I agree with Brad. Filling the rel attribute with JSON is really nasty. rel can be very useful but this is definitely not good.

DanDan at 09.10.07 / 00AM

Thanks for joining the conversation, Dan. I think your ideas are great and certainly point in the direction I would love to see most libraries go.

On a side note, I’ve had a few conversations with Alex and he seems keen to move in this direction, which is great.

Aaron GustafsonAaron Gustafson at 09.10.07 / 14PM

What we are doing with jQuery Calendar (UI Datepicker) is allowing you to add a custom XHTML namespace to define your JavaScript behaviors in XHTML element attributes.

We recommend setting your JavaScript behaviors in a separate file, but if someone needs to define their behaviors within the XHTML markup then they have that option without breaking XHTML validation – all they have to do is include our custom datepicker namespace.

Look for more details on this in our next release entitled, “UI Datepicker”.

MarcMarc at 09.10.07 / 16PM

Just mentioning WebForms 2: &lt;input type="range" value="10" min="0" max="100" step="10"&gt;

Tino ZijdelTino Zijdel at 11.10.07 / 20PM

Tino: Yes, please :) If we had WebForms 2 then most of this would be redundant but until that time I think the approach outline in this article is the best way to go. min, max, step and so on make sense for an input of type range but they don’t applied to a or a div.

DanDan at 12.10.07 / 10AM

As Brad says, rel has semantic value. And it’s also used as a signal to search engines telling them how they should treat links in a document – it’s original conception is as a means of controlling comment spam in a blog. You can use the nofollow attribute to control the importance of pages on your site and also prevent external links passing value to their target domain. Gentlemen, I refer you to the font of all debatable knowledge and also to one of nofollow’s inventors

james hoskinsjames hoskins at 16.10.07 / 09AM

ffddfffddf at 23.10.07 / 12PM

I wonder about the rel attribute, I’ve been used it for a while, holding json objects thus leaving style to css and using the rel for action…

Tercüme bürosuTercüme bürosu at 29.10.07 / 02AM

This site looks great on an iPhone!

IpodIpod at 01.11.07 / 11AM

Your LowPro got really small since prototype 1.6. :) My JavaScript missed a while Element#replaceElement, but now I know where it’s gone.

StaryStary at 21.11.07 / 00AM

tuberaceous juvavian nullify persuadably sedateness unhumanize pseudosiphuncal erwinia JP’s Contortion Site http://www.davidziller.com/

Angelo SykesAngelo Sykes at 22.11.07 / 18PM

I use lowpro, but the event selector should be changed to allow the new events from prototype (like mouse:wheel) I did a dirty patch: var parts = sel.split(/:(?=[a-z:]+$)/), css = parts.shift(), event = parts.join(’:’);

Dan PopescuDan Popescu at 30.11.07 / 15PM

Dan: Yeah, I was wondering what to do about that. I think something similar to your solution is the way forward for now. It makes the selectors look a bit ugly though:

‘dib#thing:mouse:wheel’

But hey, nevermind. I’‘ll update Low Pro now.

DanDan at 30.11.07 / 16PM

What about using :: ?

‘dib#thing::mouse:wheel’ ?

Nicolás SanguinettiNicolás Sanguinetti at 01.12.07 / 02AM

Element.update from LowPro 0.4 is incompatible with Prototype 1.6.0. Cheers.

ЕвгенийЕвгений at 08.12.07 / 18PM

About This Article