views:

2452

answers:

12

Recently I have been reading more and more about people using custom attributes in their HTML tags, mainly for the purpose of embedding some extra bits of data for use in javascript code.

I was hoping to gather some feedback on whether or not using custom attributes is a good practice, and also what some alternatives are.

It seems like it can really simplify both server side and client side code, but it also isn't W3C compliant.

Should we be making use of custom HTML attributes in our web apps? Why or why not?

For those who think custom attributes are a good thing: what are some things to keep in mind when using them?

For those who think custom attributes are bad thing: what alternatives do you use to accomplish something similar?

Update: I'm mostly interested in the reasoning behind the various methods, as well as points as to why one method is better than another. I think we can all come up with 4-5 different ways to accomplish the same thing. (hidden elements, inline scripts, extra classes, parsing info from ids, etc).

Update 2: It seems that the HTML 5 data- attribute feature has a lot of support here (and I tend to agree, it looks like a solid option). So far I haven't seen much in the way of rebuttals for this suggestion. Are there any issues/pitfalls to worry about using this approach? Or is it simply a 'harmless' invalidation of the current W3C specs?

+6  A: 

The easiest way to avoid use of custom attributes is to use existing attributes.

use meaningful, relevant class names.
For example, do something like: type='book' and type='cd', to represent books and cds. Classes are much better for representing what something IS.

e.g. class='book'

I have used custom attributes in the past, but honestly, there really isn't a need to for them if you make use of existing attributes in a semantically meaningful way.

To give a more concrete example, let's say you have a site giving links to different kinds of stores. You could use the following:

<a href='wherever.html' id='bookstore12' class='book store'>Molly's books</a>
<a href='whereverelse.html' id='cdstore3' class='cd store'>James' Music</a>

css styling could use classes like:

.store { }
.cd.store { }
.book.store { }

In the above example we see that both are links to stores (as opposed to the other unrelated links on the site) and one is a cd store, and the other is a book store.

Jonathan Fingland
Good point, but to be fair, "type" is only valid on certain tags, and when it IS a valid attribute, it also has a list of valid values, so you are still not really w3c compliant.
TM
my point was you should NOT use the type tag for this. hence the If you were...then you should... I'll edit to make that clearer
Jonathan Fingland
I tend to make my "class" attributes with flavors by having some of them appended with some type of "qualifier-". for divs related to layout only, i'd have it's class be "layout-xxx", or for internal divs that surround an important part, like a book or a store, i'd have a content-book, or content-store. then in my JavaScript, i have a function that prepends those things on the tag based on what i'm looking for. it helps keep things clean and organized for me, but requires a certain level of discipline and pre-organization.
Ape-inago
@Ape-inago: an alternative to that is to simply get elements with both classnames. e.g. $('.content.store') (jquery) or $$('.content.store') (prototype).
Jonathan Fingland
@Jonathan the double class thing works great except in cases where the 'values' are not known. For example, if it is some kind of integer id, we can't very well select for every possible case. We are then left to parse the class attribute manually which is definitely workable, but not as clear in the code, and in some cases, could be very slow (if there are a lot of candidate elements to parse).
TM
@TM: that's what the id attribute is for. In my example, see bookstore12. Obviously, this still involves extracting the numbers for the bookstore id, but that is hardly difficult. Select based on class names, and extract id numbers from *shock/horror* the id attribute.
Jonathan Fingland
@TM, if you require more complicated information that is somehow unique to each, you're probably doing it wrong. If you're doing this to supply info for an ajax request, then what happened to the form and inputs from your non-ajax version? you do support those people, right? Your non-ajax form would, presumably, have all the data you need. Stealing from that form (prior to replacing it via javascript) is obviously the easiest way to get those details
Jonathan Fingland
sadly, writing css selectors for two classes at the same time (.a.b notice the missing blank) does not work in IE. it does work in firefox and other browsers though.still, using classes is a great way to embed additional semantic meaning to your markup
knittl
@knittl: this is true only up to and including ie6. given the number of sites dropping support for or planning to soon drop support for ie6, this is really not a significant issue. Of course, it all depends on the browser usage of *your* users. Somebody working on a site accessible only through a company intranet where it is known that all (or many) users use ie6 will make a different decision on browser support than somebody designing the latest whiz-bang web 2.0 app.
Jonathan Fingland
This solution only handles 'flags', not actual data. How do handle key-value pairs, or complex data structures?
antony.trupe
@anthony: any examples that aren't covered in the post or my comments to TM?
Jonathan Fingland
A: 

Custom attributes, in my humble opinion, should not be used as they do not validate. Alternative to that, you can define many classes for a single element like:

<div class='class1 class2 class3'>
    Lorem ipsum
</div>
Alan Haggai Alavi
personally, I think this is a terrible example. your classnames define how it looks, not it's purpose. Think about when you want to change all similar divs... you'd have to go and change them all to span-11 or the like. classes should define what it IS. the style sheets should define how those things look
Jonathan Fingland
How would you use this method to specify more than just a flag? I tend to agree with your stance, and I don't use custom attributes (although I am considering it). The advantage of having a key/value pair seems quite a bit more handy than simply adding another class.
TM
@Jonathan Fingland: If Compass is used, you need not set the class names here. You can just specify them in the .sass file and your markup will be clean.
Alan Haggai Alavi
@rpflo: That is part of another debate and not relevant in Stack Overflow. You are wrong. If there is nothing good that you can comment on, why comment?
Alan Haggai Alavi
@rpflo there is no reason for trolling
TM
@Jonathan Fingland, the `class` attribute is definitely not reserved for "looks" only. Another use is "general purpose processing by user agents". Of this speaks the spec: http://www.w3.org/TR/REC-html40/struct/global.html#h-7.5.2
npup
@npup: interesting choice of quotes. As I stated over a year ago, the style sheets define how those things should look (as does the style attribute, I'll add), and the class attribute is used to define the purpose of the element. That is, it specifically is used for defining what it IS, and not how it LOOKS. I think you may have simply mis-read what I said, as we're in agreement as far as I can tell.
Jonathan Fingland
@Jonathan Fingland, I read this: "your classnames define how it looks, not it's purpose". Sorry that I misinterpreted it. But I might not be the only one who did, so maybe (since we seem to be in agreement) what I wrote can be considered a clarification for the rest of us.
npup
@npup: ahh, that may have something to do with a significant edit by the author. It used to say: `<div class='span-12 bold bordered'>`. At the time, the meaning was clearer.
Jonathan Fingland
+9  A: 

Embed the data in the class attribute and use metadata for jQuery.

All the good plug-ins support the metadata plugin(allowing per tag options).

It also allows infinitely complex data/data structures, as well as key-value pairs.

<li class="someclass {'some': 'random,'json':'data'} anotherclass">...</li>

OR

<li class="someclass" data="{'some':'random', 'json': 'data'}">...</li>

OR

<li class="someclass"><script type="data">{"some":"random","json":"data"}</script> ...</li>

Then get the data like so:

var data = $('li.someclass').metadata();
if ( data.some && data.some == 'random' )
alert('It Worked!');
antony.trupe
why the downvote?
antony.trupe
Corrupting the class attribute when there is a W3C approved way of specifying custom attributes is probably why you got voted down.
rdivilbiss
+1  A: 

I see no problem in using existing XHTML features without breaking anything or extending your namespace. Let's take a look at a small example:

<div id="some_content">
 <p>Hi!</p>
</div>

How to add additional information to some_content without additional attributes? What about adding another tag like the following?

<div id="some_content">
 <div id="some_content_extended" class="hidden"><p>Some alternative content.</p></div>
 <p>Hi!</p>
</div>

It keeps the relation via a well defined id/extension "_extended" of your choice and by its position in the hierarchy. I often use this approach together with jQuery and without actually using Ajax like techniques.

merkuro
The problem with adding nested tags like this is that it tends to create VERY cumbersome and ugly serverside code (JSP/ASP/DTL etc)
TM
A: 

Nay. Try something like this instead:

<div id="foo"/>

<script type="text/javascript">
  document.getElementById('foo').myProperty = 'W00 H00! I can add JS properties to DOM nodes without using custom attributes!';
</script>
Anon
So you prefer to write a lot of extra script tags all over your document for dynamic pages? I'd use manual javascript assignments when the info is being added on the client side, but this problem is mainly about what to render on the server. Also, jQuery.data() is much better than your method.
TM
Answer above is a framework-independent, belabored example to demonstrate the functionality. You could easily expand upon the gist of it to make the code quite terse. E.g.,<div id="foo"/><div id="bar"/><div id="baz"/><script type="text/javascript"> xtrnlFnc({ foo: 'w00 h00', bar: 'etc.', baz: 3.14159 });</script>If you're using jQuery (not that you mentioned it in your original question), by all means, use the data method--that's what it's for. If not, passing data between architectural layers is a perfectly valid use of inline script tags.
Anon
It's definitely an obvious, valid option. In my opinion it just clutters the code up much more than plenty of other alternatives that do not use custom attributes. And just to be clear, I'm not trying to be combative or rude, I am just trying to coax out some of your reasoning as why you prefer this method. You have provided an alternative but that isn't really what the question is about.
TM
can we expect the dom to not barf on custom properties?
Ape-inago
@Ape-inago yes, there's no reason it should barf, as long as we aren't using names already taken by the browser, other libraries, firefox addons, etc. Really the odds of problems are pretty low, but adding properties directly to the dom element like this (rather than a map approach) does have some small risk. These same risks ALSO exist in custom attributes however.
TM
I don't think there's a problem with this approach breaking browsers. Microsoft uses this exact mechanism as it's preferred mechanism in ASP.NET pages. (by calling RegisterExpandoAttribute on the server side). The question seems focussed on client and not the server, but on the server side all of these approaches could be (should be?) abstracted.
Adrian
The pros to this approach:--It produces valid markup (even under old browsers/specs).--It makes the intent of the data (to be consumed by JS) clear.--It is cohesive to the element without making clever use of other features (such as comments).--It does not require special parsing.From a server-side perspective, you can think of it as being like an RPC.
steamer25
A: 

I'm not doing using custom attributes, because I'm outputing XHTML, because I want the data to be machine-readable by 3rd-party software (although, I could extend the XHTML schema if I wanted to).

As an alternative to custom attributes, mostly I'm finding the id and class attributes (e.g. as mentioned in other answers) sufficient.

Also, consider this:

  • If the extra data is to be human-readable as well as machine-readable, then it needs to be encoded using (visible) HTML tags and text instead of as custom attributes.

  • If it doesn't need to be human readable, then perhaps it can be encoded using invisible HTML tags and text.

Some people make an exception: they allow custom attributes, added to the DOM by Javascript on the client side at run-time. They reckon this is OK: because the custom attributes are only added to the DOM at run-time, the HTML contains no custom attributes.

ChrisW
+52  A: 

HTML 5 explicitly allows custom attributes that begin with data. So, for example, <p data-date-changed="Jan 24 5:23 p.m.">Hello</p> is valid. Since it's officially supported by a standard, I think this is the best option for custom attributes. And it doesn't require you to overload other attributes with hacks, so your HTML can stay semantic.

Chuck
This is a good approach.. But I doubt it will work of you have to support IE 6 and other old browsers.
roosteronacid
I'm pretty sure it does work with older browsers; the attributes are added to the DOM, where you can access them.
Ms2ger
It works perfectly well with all browsers using the getAttribute() method on an HTMLElement. Plus, as HTML5 dataset support grows you can easily add that in.
ajm
This answer is exactly what I was looking for, thanks.
Odd
Although HTML5 is NOT standard yet, I agree with ya...
Pablo Cabrera
HTML5 is not a standard. Also, HTML 4.01 allows for "custom attributes."
Wahnfrieden
@Wahnfrieden: Firstly, I don't think it's fruitful to split hairs. Second, I don't see anywhere in the HTML 4.01 spec that specifically allows for custom attributes. Can you be more specific about where it's allowed?
Chuck
@Chuck apparently you can add Attributes to the DOCTYPE: http://www.rodsdot.com/html/Specifying-Custom-HTML-Attributes-With-A-Custom-DOCTYPE.asp - not that I think it's a good idea, but it seems standardized.
Michael Stum
A: 

Spec: Create an ASP.NET TextBox control which dynamically auto-formats its text as a number, according to properties "DecimalSeperator" and "ThousandsSeperator", using JavaScript.


One way to transfer these properties from the control to JavaScript is to have the control render out custom properties:

<input type="text" id="" decimalseperator="." thousandsseperator="," />

Custom properties are easily accessible by JavaScript. And whilst a page using elements with custom properties won't validate, the rendering of that page won't be affected.


I only use this approach when I want to associate simple types like strings and integers to HTML elements for use with JavaScript. If I want to make HTML elements easier to identify, I'll make use of the class and id properties.

roosteronacid
+36  A: 

Here's a technique I've been using recently:

<div id="someelement">

    <!-- {
        someRandomData: {a:1,b:2},
        someString: "Foo"
    } -->

    <div>... other regular content...</div>
</div>

The comment-object ties to the parent element (i.e. #someelement).

Here's the parser: http://pastie.org/511358

To get the data for any particular element simply call parseData with a reference to that element passed as the only argument:

var myElem = document.getElementById('someelement');

var data = parseData( myElem );

data.someRandomData.a; // <= Access the object staight away


It can be more succinct than that:

<li id="foo">
    <!--{specialID:245}-->
    ... content ...
</li>

Access it:

parseData( document.getElementById('foo') ).specialID; // <= 245


The only disadvantage of using this is that it cannot be used with self-closing elements (e.g. <img/>), since the comments must be within the element to be considered as that element's data.


EDIT:

Notable benefits of this technique:

  • Easy to implement
  • Does not invalidate HTML/XHTML
  • Easy to use/understand (basic JSON notation)
  • Unobtrusive and semantically cleaner than most alternatives


Here's the parser code (copied from the http://pastie.org/511358 hyperlink above, in case it ever becomes unavailable on pastie.org):

var parseData = (function(){

    var getAllComments = function(context) {

            var ret = [],
                node = context.firstChild;

            if (!node) { return ret; }

            do {
                if (node.nodeType === 8) {
                    ret[ret.length] = node;
                }
                if (node.nodeType === 1) {
                    ret = ret.concat( getAllComments(node) );
                }
            } while( node = node.nextSibling );

            return ret;

        },
        cache = [0],
        expando = 'data' + +new Date(),
        data = function(node) {

            var cacheIndex = node[expando],
                nextCacheIndex = cache.length;

            if(!cacheIndex) {
                cacheIndex = node[expando] = nextCacheIndex;
                cache[cacheIndex] = {};
            }

            return cache[cacheIndex];

        };

    return function(context) {

        context = context || document.documentElement;

        if ( data(context) && data(context).commentJSON ) {
            return data(context).commentJSON;
        }

        var comments = getAllComments(context),
            len = comments.length,
            comment, cData;

        while (len--) {
            comment = comments[len];
            cData = comment.data.replace(/\n|\r\n/g, '');
            if ( /^\s*?\{.+\}\s*?$/.test(cData) ) {
                try {
                    data(comment.parentNode).commentJSON =
                        (new Function('return ' + cData + ';'))();
                } catch(e) {}
            }
        }

        return data(context).commentJSON || true;

    };

})();
J-P
Interesting take on the matter, this is one I hadn't thought of before.
TM
Very interesting...
Paolo Bergantino
Out of curiosity, what method do you use for self-closing tags? I generally need to use something like this on <input> elements (to aid in client-side validation rules). What alternative do you take in that situation?
TM
I'd probably use a similar technique, instead of the comment data tying to the "parentNode" it could tie to the "previousSibling" of the comment... Then you could have the comment immediately following the <input/> and it would work: <input/><!--{data:123}-->
J-P
someone should make this a jquery plugin
SeanDowney
Would using `display:none` be better than using a comment tag? Or even better, create a CSS class, say `data`, that has `display:none` and just search for elements with the class name `data`.
trinithis
A: 

For complex web apps, I drop custom attributes all over the place.

For more public facing pages I use the "rel" attribute and dump all my data there in JSON and then decode it with MooTools or jQuery:

<a rel="{color:red, awesome:true, food: tacos}">blah</a>.

I'm trying to stick with HTML 5 data attribute lately just to "prepare", but it hasn't come naturally yet.

rpflo
+1  A: 

We've made a web-based editor that understands a subset of HTML - a very strict subset (that understood nearly universally by mail clients). We need to express things like <td width="@INSWIDTH_42@"> in the database, but we can't have that in the DOM, otherwise the browser where the editor runs, freaks out (or is more likely to freak out than it is likely to freak out over custom attributes). We wanted drag-and-drop, so putting it purely in the DOM was out, as was jquery's .data() (the extra data didn't get copied properly). We probably also needed the extra data to come along for the ride in .html(). In the end we settled on using <td width="1234" rs-width="@INSWIDTH_42@"> during the editing process, and then when we POST it all, we remove width and do a regex search-and-destroy s/rs-width=/width=/g.

At first the guy writing most of this was the validation-nazi on this issue and tried everything to avoid our custom attribute, but in the end acquiesced when nothing else seemed to work for ALL our requirements. It helped when he realized that the custom attribute would never appear in an email We did consider encoding our extra data in class, but decided that would be the greater of two evils.

Personally, I prefer to have things clean and passing validators etc., but as a company employee I have to remember that my primary responsibility is advancing the company's cause (making as much money as quickly as possible), not that of my egotistical desire for technical purity. Tools should work for us; not us for them.

Bernd Jendrissek
+3  A: 

You can create any attribute if you specify a schema for your page.

For example:

Addthis

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:addthis="http://www.addthis.com/help/api-spec"&gt;
...
<a addthis:title="" addthis:url="" ...>

Facebook (even tags)

<html xmlns:og="http://opengraphprotocol.org/schema/" xmlns:fb="http://www.facebook.com/2008/fbml"&gt;
...
<fb:like href="http://developers.facebook.com/" width="450" height="80"/>
BrunoLM