Atributos personalizados: ¿sí o no?
Recientemente he estado leyendo más y más sobre personas que utilizan atributos personalizados en sus etiquetas HTML, principalmente con el fin de incrustar algunos bits de datos adicionales para su uso en código javascript.
Esperaba recopilar comentarios sobre si el uso de atributos personalizados es una buena práctica o no, y también cuáles son algunas alternativas.
Parece que realmente puede simplificar el código tanto del lado del servidor como del lado del cliente, pero tampoco es compatible con el W3C.
¿Deberíamos utilizar atributos HTML personalizados en nuestras aplicaciones web? ¿Por qué o por qué no?
Para aquellos que piensan que los atributos personalizados son algo bueno: ¿qué cosas deben tener en cuenta al usarlos?
Para aquellos que piensan que los atributos personalizados son malos: ¿qué alternativas utilizan para lograr algo similar?
Actualización: lo que más me interesa es el razonamiento detrás de los distintos métodos, así como los puntos de por qué un método es mejor que otro. Creo que a todos se nos ocurren 4 o 5 formas diferentes de lograr lo mismo. (elementos ocultos, scripts en línea, clases adicionales, análisis de información de identificadores, etc.).
Actualización 2: Parece que la data-
función de atributo HTML 5 tiene mucho soporte aquí (y tiendo a estar de acuerdo, parece una opción sólida). Hasta ahora no he visto muchas refutaciones a esta sugerencia. ¿Hay algún problema/obstáculo del que preocuparse al utilizar este enfoque? ¿O se trata simplemente de una invalidación "inofensiva" de las especificaciones actuales del W3C?
HTML 5 permite explícitamente atributos personalizados que comienzan con data
. Así, por ejemplo, <p data-date-changed="Jan 24 5:23 p.m.">Hello</p>
es válido. Dado que está oficialmente respaldado por un estándar, creo que esta es la mejor opción para atributos personalizados. Y no requiere que sobrecargues otros atributos con trucos, por lo que tu HTML puede seguir siendo semántico.
Fuente: http://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes
Aquí hay una técnica que he estado usando recientemente:
<div id="someelement">
<!-- {
someRandomData: {a:1,b:2},
someString: "Foo"
} -->
<div>... other regular content...</div>
</div>
El objeto comentario se vincula al elemento padre (es decir, #algúnelemento).
Aquí está el analizador: http://pastie.org/511358
Para obtener los datos de cualquier elemento en particular, simplemente llame parseData
con una referencia a ese elemento pasado como único argumento:
var myElem = document.getElementById('someelement');
var data = parseData( myElem );
data.someRandomData.a; // <= Access the object staight away
Puede ser más conciso que eso:
<li id="foo">
<!--{specialID:245}-->
... content ...
</li>
Acceder a él:
parseData( document.getElementById('foo') ).specialID; // <= 245
La única desventaja de usar esto es que no se puede usar con elementos de cierre automático (por ejemplo <img/>
), ya que los comentarios deben estar dentro del elemento para ser considerados como datos de ese elemento.
EDITAR :
Beneficios notables de esta técnica:
- Fácil de implementar
- No invalida HTML/XHTML
- Fácil de usar/comprender (notación JSON básica)
- Discreto y semánticamente más limpio que la mayoría de las alternativas
Aquí está el código del analizador (copiado del hipervínculo http://pastie.org/511358 anterior, en caso de que alguna vez deje de estar disponible en 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;
};
})();
La forma más sencilla de evitar el uso de atributos personalizados es utilizar atributos existentes.
Utilice nombres de clases significativos y relevantes.
Por ejemplo, haga algo como: type='book'
y type='cd'
, para representar libros y CD. Las clases son mucho mejores para representar lo que algo ES .
p.ejclass='book'
He usado atributos personalizados en el pasado, pero honestamente, realmente no son necesarios si se utilizan atributos existentes de una manera semánticamente significativa.
Para dar un ejemplo más concreto, digamos que tiene un sitio que ofrece enlaces a diferentes tipos de tiendas. Podrías usar lo siguiente:
<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>
El estilo CSS podría usar clases como:
.store { }
.cd.store { }
.book.store { }
En el ejemplo anterior vemos que ambos son enlaces a tiendas (a diferencia de los otros enlaces no relacionados en el sitio) y uno es una tienda de CD y el otro es una librería.
Incruste los datos en el dom y use metadatos para jQuery .
Todos los buenos complementos admiten el complemento de metadatos (lo que permite opciones por etiqueta).
También permite estructuras de datos/datos infinitamente complejas, así como pares clave-valor.
<li class="someclass {'some': 'random,'json':'data'} anotherclass">...</li>
O
<li class="someclass" data="{'some':'random', 'json': 'data'}">...</li>
O
<li class="someclass"><script type="data">{"some":"random","json":"data"}</script> ...</li>
Luego obtenga los datos así:
var data = $('li.someclass').metadata();
if ( data.some && data.some == 'random' )
alert('It Worked!');
No veo ningún problema en utilizar las funciones XHTML existentes sin romper nada ni ampliar el espacio de nombres. Veamos un pequeño ejemplo:
<div id="some_content">
<p>Hi!</p>
</div>
¿Cómo agregar información adicional a some_content sin atributos adicionales? ¿Qué tal si agregamos otra etiqueta como la siguiente?
<div id="some_content">
<div id="some_content_extended" class="hidden"><p>Some alternative content.</p></div>
<p>Hi!</p>
</div>
Mantiene la relación a través de una identificación/extensión bien definida "_extendida" de su elección y por su posición en la jerarquía. A menudo uso este enfoque junto con jQuery y sin usar técnicas similares a Ajax.