Obtenga una lista de atributos data- * usando javascript / jQuery

150

Dado un elemento HTML arbitrario con cero o más data-*atributos, ¿cómo se puede recuperar una lista de pares clave-valor para los datos?

Por ejemplo, dado esto:

<div id='prod' data-id='10' data-cat='toy' data-cid='42'>blah</div>

Me gustaría poder recuperar esto programáticamente:

{ "id":10, "cat":"toy", "cid":42 }

Usando jQuery (v1.4.3), acceder a los bits de datos individuales usando $.data()es simple si las claves se conocen de antemano, pero no es obvio cómo se puede hacer con conjuntos arbitrarios de datos.

Estoy buscando una solución 'simple' de jQuery si existe, pero de lo contrario no me importaría un enfoque de nivel inferior. Intenté analizar, $('#prod').attributespero mi falta de javascript-fu me decepciona.

actualizar

customdata hace lo que necesito. Sin embargo, incluir un complemento jQuery solo por una fracción de su funcionalidad parecía una exageración.

Observando la fuente me ayudó a arreglar mi propio código (y mejoró mi javascript-fu).

Aquí está la solución que se me ocurrió:

function getDataAttributes(node) {
    var d = {}, 
        re_dataAttr = /^data\-(.+)$/;

    $.each(node.get(0).attributes, function(index, attr) {
        if (re_dataAttr.test(attr.nodeName)) {
            var key = attr.nodeName.match(re_dataAttr)[1];
            d[key] = attr.nodeValue;
        }
    });

    return d;
}

actualización 2

Como se demostró en la respuesta aceptada, la solución es trivial con jQuery (> = 1.4.4). $('#prod').data()devolvería la información requerida dict.

Shawn Chin
fuente
Dirigiéndose a la "actualización 2", tenga en cuenta que jquery data () usa algo de caché interna y los datos (clave, valor) no se propagan a DOM, lo que puede causar muchos dolores de cabeza. También desde jquery 3 data () convierte el atributo data-some-thing = "test" en {someThing: "test"}
ETNyx

Respuestas:

96

En realidad, si está trabajando con jQuery, a partir de la versión 1.4.31.4.4 (debido al error como se menciona en los comentarios a continuación), los data-*atributos son compatibles a través de .data():

A partir de jQuery 1.4.3, los data- atributos HTML 5 se incorporarán automáticamente al objeto de datos de jQuery.

Tenga en cuenta que las cadenas se dejan intactas mientras que los valores de JavaScript se convierten a su valor asociado (esto incluye booleanos, números, objetos, matrices y valores nulos). Los data- atributos se extraen la primera vez que se accede a la propiedad de datos y luego ya no se accede ni se muta (todos los valores de datos se almacenan internamente en jQuery).

La jQuery.fn.datafunción devolverá todos los data-atributos dentro de un objeto como pares clave-valor, siendo la clave la parte del nombre del atributo después data-y el valor siendo el valor de ese atributo después de convertirse siguiendo las reglas indicadas anteriormente.

También he creado una demostración simple si eso no te convence: http://jsfiddle.net/yijiang/WVfSg/

Yi Jiang
fuente
3
No, esto se divide en 1.4.3 . Se requiere explícitamente un mínimo de 1.4.4.
Crescent Fresh el
Gracias. Eso es exactamente lo que estaba buscando. Previamente lo intenté con 1.4.3 y no fui muy lejos. La demostración de jsfiddle también ayudó.
Shawn Chin el
2
@Isc: una cosa que es muy molesto es el intento de jQuery en "deserializar" el valor encontrado en el atributo (es decir, de maestro : !jQuery.isNaN( data ) ? parseFloat( data ) : ...). Tenía números de serie del producto en mi atributo, como el data-serial="00071134"que jQuery transformó en un número 71134, lo que me obligó a volver a lo menos elegante .attr('data-serial')(no es una opción en su caso). He visto preguntas sobre SO que han expresado frustraciones similares wrt .data(), intentaré desenterrar una ...
Crescent Fresh
@CrescentFresh: Buen punto. Definitivamente algo que debo tener en cuenta. En mi solución ( gist.github.com/701652 ) vuelvo a analizar node.attributes cuando jQuery <1.4.3; Con este problema en mente, quizás debería simplemente seguir analizando manualmente los atributos.
Shawn Chin el
77
Tenga en cuenta que $.data()también devuelve todo lo que ha almacenado usando $.data(key, value). Si realmente desea verificar si algo está realmente almacenado en el marcado como un atributo data- *, este método podría no ser la mejor opción.
Philip Walton
81

También debería ofrecerse una solución de JavaScript pura, ya que la solución no es difícil:

var a = [].filter.call(el.attributes, function(at) { return /^data-/.test(at.name); });

Esto proporciona una matriz de objetos de atributos, que tienen namey valuepropiedades:

if (a.length) {
    var firstAttributeName = a[0].name;
    var firstAttributeValue = a[0].value;
}

Editar: para ir un paso más allá, puede obtener un diccionario iterando los atributos y rellenando un objeto de datos:

var data = {};
[].forEach.call(el.attributes, function(attr) {
    if (/^data-/.test(attr.name)) {
        var camelCaseName = attr.name.substr(5).replace(/-(.)/g, function ($0, $1) {
            return $1.toUpperCase();
        });
        data[camelCaseName] = attr.value;
    }
});

Entonces podría acceder al valor de, por ejemplo, data-my-value="2"como data.myValue;

jsfiddle.net/3KFYf/33

Editar: si desea establecer atributos de datos en su elemento mediante programación desde un objeto, puede:

Object.keys(data).forEach(function(key) {
    var attrName = "data-" + key.replace(/[A-Z]/g, function($0) {
        return "-" + $0.toLowerCase();
    });
    el.setAttribute(attrName, data[key]);
});

jsfiddle.net/3KFYf/34

EDITAR: si está usando babel o TypeScript, o está codificando solo para los navegadores es6, este es un buen lugar para usar las funciones de flecha es6 y acortar un poco el código:

var a = [].filter.call(el.attributes, at => /^data-/.test(at.name));
gilly3
fuente
¡Buena respuesta! La primera línea no reemplaza el -carácter como lo hace jQuery, pero la respuesta editada sí.
Timo Huovinen
@ gilly3 Su jsfiddle no parece funcionar. ¿Puedes echar un vistazo?
Ethan
1
@ Ethan - Corregido. Gracias por hacérmelo saber. Había estado usando beautify.js de jsbeautifier.org para imprimir bastante el JSON. Aparentemente ese enlace está roto ahora. Sin embargo, eso era una exageración ya JSON.stringify()ha JSON formatea en.
gilly3
@ gilly3 Fantástico, tu rutina es genial para buscar todos. ¿Sería fácil agregar datos de recuperación para una clave dada, así como actualizar datos modificados para clave (s)? Si puedes compartir, sería genial.
Ethan
@Ethan - Para obtener un solo valor, no overthink: el.getAttribute("data-foo"). He actualizado mi respuesta para mostrar cómo puede establecer atributos de datos basados ​​en un objeto.
gilly3
22

Echa un vistazo aquí :

Si el navegador también es compatible con la API de JavaScript HTML5, debería poder obtener los datos con:

var attributes = element.dataset

o

var cat = element.dataset.cat

Ah, pero también leí:

Desafortunadamente, la nueva propiedad del conjunto de datos aún no se ha implementado en ningún navegador, por lo tanto, mientras tanto, es mejor usar getAttributey setAttributecomo se demostró anteriormente.

Es de mayo de 2010.


Si utiliza jQuery todos modos, es posible que desee echar un vistazo a la CustomData plugin. Sin embargo, no tengo experiencia con eso.

Felix Kling
fuente
También de interés, de ppk: quirksmode.org/dom/w3c_core.html#attributes
Pointy
Gracias felix Me había encontrado con datos personalizados. Desafortunadamente, no hace lo que necesito, es decir, obtener todos los datos dado que las claves no se conocen de antemano.
Shawn Chin el
@lsc: La documentación dice que $("#my").customdata()debería darte {method: "delete", remote: "true"}... así que debería funcionar.
Felix Kling
Usar un complemento de jQuery por separado parece una exageración, pero mirar la fuente de datos personalizados me ayudó a solucionar mis propias soluciones. Aceptará su respuesta y actualizará Q con la función de trabajo.
Shawn Chin el
1
@lsc: Totalmente bien, las soluciones integradas siempre deberían preferirse en mi opinión. Desafortunadamente, no estoy actualizado con el último desarrollo de jQuery;) Me pregunto por qué alguien me rechazó ...
Felix Kling
5

Como se mencionó anteriormente, los navegadores modernos tienen la API The HTMLElement.dataset .
Esa API le proporciona un DOMStringMap , y puede recuperar la lista de data-*atributos simplemente haciendo:

var dataset = el.dataset; // as you asked in the question

También puede recuperar una matriz con los data-nombres clave de la propiedad como

var data = Object.keys(el.dataset);

o mapear sus valores por

Object.keys(el.dataset).map(function(key){ return el.dataset[key];});
// or the ES6 way: Object.keys(el.dataset).map(key=>{ return el.dataset[key];});

y de esta manera puedes iterarlos y usarlos sin la necesidad de filtrar entre todos los atributos del elemento como lo necesitábamos antes .

Sergio
fuente
3

o gilly3la excelente respuesta de convert a un método jQuery:

$.fn.info = function () {
    var data = {};
    [].forEach.call(this.get(0).attributes, function (attr) {
        if (/^data-/.test(attr.name)) {
            var camelCaseName = attr.name.substr(5).replace(/-(.)/g, function ($0, $1) {
                return $1.toUpperCase();
            });
            data[camelCaseName] = attr.value;
        }
    });
    return data;
}

Uso: $('.foo').info();

PHPst
fuente
2

Debería obtener los datos a través de los atributos del conjunto de datos

var data = element.dataset;

conjunto de datos es una herramienta útil para obtener atributos de datos

lychi
fuente
IIRC, this.datasettodavía no funciona en todos los navegadores. Sin embargo, hay un complemento jquery que proporciona esta función.
Shawn Chin el
1

Puede iterar sobre los atributos de datos como cualquier otro objeto para obtener claves y valores, aquí le mostramos cómo hacerlo $.each:

    $.each($('#myEl').data(), function(key, value) {
        console.log(key);
        console.log(value);
    });
Andrés
fuente
1

Utilizo cada uno anidado , para mí esta es la solución más fácil (Fácil de controlar / cambiar "lo que haces con los valores, en mi ejemplo, salida de atributos de datos como ul-list) (Código Jquery)

var model = $(".model");

var ul = $("<ul>").appendTo("body");

$(model).each(function(index, item) {
  ul.append($(document.createElement("li")).text($(this).text()));
  $.each($(this).data(), function(key, value) {
    ul.append($(document.createElement("strong")).text(key + ": " + value));
    ul.append($(document.createElement("br")));
  }); //inner each
  ul.append($(document.createElement("hr")));
}); // outer each

/*print html*/
var htmlString = $("ul").html();
$("code").text(htmlString);
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/prism.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/themes/prism-okaidia.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1 id="demo"></h1>

<ul>
  <li class="model" data-price="45$" data-location="Italy" data-id="1234">Model 1</li>
  <li class="model" data-price="75$" data-location="Israel" data-id="4321">Model 2</li> 
  <li class="model" data-price="99$" data-location="France" data-id="1212">Model 3</li> 
</ul>

<pre>
<code class="language-html">
  
</code>
</pre>

<h2>Generate list by code</h2>
<br>

Codepen: https://codepen.io/ezra_siton/pen/GRgRwNw?editors=1111

Ezra Siton
fuente
0

Una forma de encontrar todos los atributos de datos es usar element.attributes. Utilizando .attributes, puede recorrer todos los atributos del elemento, filtrando los elementos que incluyen la cadena "datos-".

let element = document.getElementById("element");

function getDataAttributes(element){
    let elementAttributes = {},
        i = 0;

    while(i < element.attributes.length){
        if(element.attributes[i].name.includes("data-")){
            elementAttributes[element.attributes[i].name] = element.attributes[i].value
        }
        i++;
    }

    return elementAttributes;

}
Jmorel88
fuente