Custom attributes - Yea or nay?
Asked Answered
H

15

262

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?

Hippie answered 14/6, 2009 at 3:52 Comment(9)
Honestly, my initial stance is that they're not such a bad thing, which can be rather controversial with the purists. I feel like I really need to sit down and evaluate all the options available to properly back this up, though, thus the need to write the long essay.Urial
To do that you may need only some counter-example[s]: of what you're trying to implement, how it's convenient to do that with custom attributes, and why that solution better and not worse than other solutions without custom attributes.Smog
@Smog I am asking mostly out of interest, not out of some specific application.Hippie
Well, there's a lot of options for getting the data to the client side: hidden input fields, hidden definition lists, classes, metadata plugins, having a huge Javascript dictionary (object) with all the data mapping separately, custom attributes, data attributes (HTML5), etc. I want to explore all of these, consider their merits, their pitfalls, and finally reach a conclusion. This post did finally get me to start writing this. :) Should be done sometime before 2010.Urial
@Paolo I am in the same boat, I can think of a LOT of alternatives but nothing stands out as the best. I'd like something that is #1 easy to understand for coworkers, #2 clean/clear code when dynamically rendering on the server side (in a JSP for example), #3 clean/clear to work with on the clientside (in javascript). Frankly W3C specs aren't as important to me as maintainability / strength.Hippie
This question is very similar to this: stackoverflow.com/questions/432174 and this stackoverflow.com/questions/209428 - since they all have lots of good answers, I propose a merge... how do we do that?Conferva
@Paolo you can't just say you wrote an essay answering this question without giving us the link. Not cool.Galatia
Here's on how to set and get these variables via JavaScript developer.mozilla.org/en-US/docs/Web/Guide/HTML/…Chokecherry
Possible duplicate of Non-Standard Attributes on HTML Tags. Good Thing? Bad Thing? Your Thoughts?Stefaniestefano
P
260

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.

Source: http://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes

Phosphor answered 14/6, 2009 at 6:32 Comment(7)
This is a good approach.. But I doubt it will work of you have to support IE 6 and other old browsers.Reiterant
I'm pretty sure it does work with older browsers; the attributes are added to the DOM, where you can access them.Electrode
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.Kitti
HTML5 is not a standard. Also, HTML 4.01 allows for "custom attributes."Abbotsun
@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?Phosphor
@Phosphor apparently you can add Attributes to the DOCTYPE: rodsdot.com/html/… - not that I think it's a good idea, but it seems standardized.Kerguelen
@Wahnfrieden: w3.org/TR/REC-html40/intro/sgmltut.html#idx-attribute-8 which is the approved, standards compliant method. Which is well described and demonstrated here: rodsdot.com/html/… As previously posted by others.Turpitude
F
97

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;

    };

})();
Fruitless answered 14/6, 2009 at 8:50 Comment(11)
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?Hippie
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}-->Fruitless
someone should make this a jquery pluginFrontality
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.Awestricken
This would have to be the most interesting and impressive answers I've ever seen on Stack Overflow. +1 Thanks, @J-P. :)Photoluminescence
I can see this being useful if you want to store a more complex object, but I have found that I generally only need one or two properties to do an ajax call to get the full object. Because of this, I dont think I would use this method.Heresiarch
not enough jquery :) Just joking, this could be useful if you can't still use (for whatever reason) html5Apologetic
Comments should be able to be changed/deleted without breaking anything. That's the whole point. So it's a bad idea to put anything important to the markup or code into comments. Future developers could easily think that they are comments and delete them. We already have a real solution to this question: custom attributes prefixed with "data-". This approach should never be used.Manzoni
Let me reinforce @Manzoni 's statement: don't use comments for adding functionality. Specially in the HTML. Aren't you using minifiers? You won't be able to remove comments without breaking your code. This also means you can't add real comments anymore.Chessman
that's incredible, you literally invented real attributes/annotations that don't break in xhtml. also it being a comment shouldn't matter since you treat it as an annotation, not just a comment. also you shouldn't trust minifiers regardless, if you do just preserve comments and run your own preprocessor on it.Taxiway
on a seperate note, you should just use a class and map the uuid class to the attributes, it should be cheaper overall then calling a parserTaxiway
H
16

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">
...
<a addthis:title="" addthis:url="" ...>

Facebook (even tags)

<html xmlns:og="http://opengraphprotocol.org/schema/" xmlns:fb="http://www.facebook.com/2008/fbml">
...
<fb:like href="http://developers.facebook.com/" width="450" height="80"/>
Habile answered 22/7, 2010 at 2:36 Comment(2)
So you have to preface them with addthis or xmlns?Mastiff
How is this done in 2022? CustomElementRegistry.define()? developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/…Mastiff
C
10

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.

Clementclementas answered 14/6, 2009 at 3:57 Comment(11)
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.Hippie
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 clearerClementclementas
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.Faxun
@Ape-inago: an alternative to that is to simply get elements with both classnames. e.g. $('.content.store') (jquery) or $$('.content.store') (prototype).Clementclementas
@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).Hippie
@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.Clementclementas
@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 detailsClementclementas
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 markupGauguin
@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.Clementclementas
This solution only handles 'flags', not actual data. How do handle key-value pairs, or complex data structures?Poussette
@anthony: any examples that aren't covered in the post or my comments to TM?Clementclementas
P
6

Embed the data in the dom 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!');
Poussette answered 14/6, 2009 at 4:5 Comment(3)
Corrupting the class attribute when there is a W3C approved way of specifying custom attributes is probably why you got voted down.Turpitude
corrupting the class attribute is only one of the ways to use the plugin; it is not the only way.Poussette
Another reason why you got voted down is suggesting a plugin where no plugin is needed at all.Languor
O
4

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.

Ollayos answered 14/6, 2009 at 4:11 Comment(1)
The problem with adding nested tags like this is that it tends to create VERY cumbersome and ugly serverside code (JSP/ASP/DTL etc)Hippie
E
4

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>
Engird answered 14/6, 2009 at 4:12 Comment(8)
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.Hippie
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.Engird
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.Hippie
can we expect the dom to not barf on custom properties?Faxun
@Faxun 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.Hippie
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.Garay
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.Nicolanicolai
As old as this answer is, I have to thank you! I'm one of those rare birds that likes to support browsers at least back to the last ones available for Win-XP, which unfortunately includes IE8. Thus, I can't use newer features line "setAttibute" or the well liked "HTML 5 data-". The only thing I suppose I need to do is use names for 'myProperty' items that have a low likelihood of clashing with a future existing property. Shouldn't be too hard. :-)Liveried
S
2

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.

Smog answered 14/6, 2009 at 4:12 Comment(0)
B
2

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.

Baku answered 12/5, 2010 at 14:16 Comment(1)
Aside from technical purity, what about real-world risks? Imo, the formal spec will never use "rs-" as a prefix for attributes, so i think that's a non-issue. What about other systems that use your code?Mastiff
C
2

I know people are against it, but I came up with a super short solution for this. If you want to use a custom attribute like "mine" so for example:

<a href="test.html" mine-one="great" mine-two="awesome">Test</a>

Then you can run this code to get an object back just like jquery.data() does.

var custom_props = {} ;
$.each($(".selector")[0].attributes, function(i,x) {
    if (this.specified && x.name.indexOf("mine-") !== -1) 
        self.new_settings[x.name.replace("modal-","")] = x.value;
});
Cottonwood answered 19/4, 2017 at 20:42 Comment(0)
I
1

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.

Idealize answered 14/6, 2009 at 23:36 Comment(1)
What if it's a public facing complex web app?Mastiff
R
0

Spec: Create an ASP.NET TextBox control which dynamically auto-formats its text as a number, according to properties "DecimalSeparator" and "ThousandsSeparator", 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="" decimalseparator="." thousandsseparator="," />

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.

Reiterant answered 14/6, 2009 at 7:7 Comment(0)
A
0

I use custom fields all the time for example <a i="" .... Then reference to i with jquery. Invalid html , yes. It works well, yes.

Anatropous answered 26/9, 2015 at 0:23 Comment(2)
Something looks like its missing here. Was your tag complete, here?Stature
How can anyone comprehend this? Please complete your answer.Cini
M
0

Contrary to answers which say custom attributes won't validate:

Custom attributes will validate.

So will custom tags, as long as the custom tags are lowercase and hyphenated.

Try this in any validator. It will validate.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Custom Test</title>
    </head>
    <body>
        <dog-cat PIANO="yellow">test</dog-cat>
    </body>
</html>

Some validators:

https://appdevtools.com/html-validator

https://www.freeformatter.com/html-validator.html

https://validator.w3.org/nu/

The question is: Is it safe? Will it break later?

Custom Tags

No hyphenated tags exist. I believe that W3C will never use a hyphenated tag. And if they did, as long as you use an uncommon prefix, you'll never see a conflict. Eg.<johny-mytag>.

Custom Attributes

There are hyphenated HTML attributes. But the HTML spec promises never to use an attribute starting with data-. So data-myattrib is guaranteed to be safe. However, i believe that W3C will never introduce any attribute that starts with johny-. As long as your prefix is unusual, you'll never see a conflict.

Mastiff answered 2/12, 2022 at 4:52 Comment(0)
A
-2

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>
Aquinas answered 14/6, 2009 at 3:58 Comment(7)
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 lookClementclementas
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.Hippie
@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.Aquinas
@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: w3.org/TR/REC-html40/struct/global.html#h-7.5.2Sukey
@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.Clementclementas
@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.Sukey
@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.Clementclementas

© 2022 - 2024 — McMap. All rights reserved.