Obter lista de atributos de dados- * usando javascript / jQuery

150

Dado um elemento HTML arbitrário com zero ou mais data-*atributos, como recuperar uma lista de pares de valores-chave para os dados.

Por exemplo, dado o seguinte:

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

Eu gostaria de poder recuperar programaticamente isso:

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

Usando o jQuery (v1.4.3), acessar os bits individuais de dados usando $.data()é simples se as chaves forem conhecidas antecipadamente, mas não é óbvio como alguém pode fazer isso com conjuntos arbitrários de dados.

Estou procurando uma solução jQuery 'simples', se houver, mas não se importaria com uma abordagem de nível inferior. Eu tentei analisar, $('#prod').attributesmas minha falta de javascript-fu está me decepcionando.

atualizar

customdata faz o que eu preciso. No entanto, incluir um plugin jQuery apenas por uma fração de sua funcionalidade parecia um exagero.

Observar a fonte me ajudou a corrigir meu próprio código (e melhorou meu javascript-fu).

Aqui está a solução que eu vim com:

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;
}

atualização 2

Como demonstrado na resposta aceita, a solução é trivial com o jQuery (> = 1.4.4). $('#prod').data()retornaria o ditado de dados necessário.

Shawn Chin
fonte
A adição "atualização 2" está ciente dos dados do jquery (), usa algum cache interno e os dados (chave, valor) não se propagam para o DOM, o que pode causar muitas dores de cabeça. Também desde que o jquery 3 data () converte o atributo data-some-thing = "test" em {someThing: "test"}
ETNyx

Respostas:

96

Na verdade, se você estiver trabalhando com jQuery, a partir da versão 1.4.31.4.4 (devido ao erro mencionado nos comentários abaixo), os data-*atributos são suportados por .data():

A partir do jQuery 1.4.3, os data- atributos HTML 5 serão automaticamente puxados para o objeto de dados do jQuery.

Observe que as strings são deixadas intactas enquanto os valores JavaScript são convertidos no valor associado (isso inclui booleanos, números, objetos, matrizes e nulos). Os data- atributos são extraídos na primeira vez que a propriedade de dados é acessada e, em seguida, não são mais acessados ​​ou alterados (todos os valores de dados são armazenados internamente no jQuery).

A jQuery.fn.datafunção retornará todo o data-atributo dentro de um objeto como pares de valor-chave, sendo a chave a parte do nome do atributo data-e o valor o valor desse atributo após a conversão, seguindo as regras mencionadas acima.

Também criei uma demonstração simples, se isso não o convencer: http://jsfiddle.net/yijiang/WVfSg/

Yi Jiang
fonte
3
Não, isso está quebrado no 1.4.3 . É exigido explicitamente um mínimo de 1.4.4.
Crescent Fresh
Obrigado. Era exatamente o que eu estava procurando. Eu tentei anteriormente com o 1.4.3 e não fui muito longe. A demonstração do jsfiddle também ajudou.
Shawn Chin
2
@Isc: uma coisa que é muito chato é a tentativa da jQuery em "deserializing" o valor encontrado no atributo (ou seja, do mestre : !jQuery.isNaN( data ) ? parseFloat( data ) : ...). Eu tinha números de série do produto em meu atributo, como data-serial="00071134"qual jQuery se transformou em um número 71134, forçando-me a voltar ao menos elegante .attr('data-serial')(não é uma opção no seu caso). Eu já vi perguntas sobre SO que expressaram frustrações semelhantes .data(), tentarão desenterrar uma delas ...
Crescent Fresh
@CrescentFresh: Bom ponto. Definitivamente algo que eu deveria prestar atenção. Na minha solução ( gist.github.com/701652 ), volto a analisar node.attributes quando jQuery <1.4.3; com esse problema em mente, talvez eu devesse simplesmente analisar manualmente os atributos.
Shawn Chin
7
Lembre-se de que $.data()também retorna tudo o que você armazenou usando $.data(key, value). Se você realmente deseja verificar se algo está realmente armazenado na marcação como um atributo data- *, esse método pode não ser a melhor opção.
Philip Walton
81

Uma solução JavaScript pura também deve ser oferecida, pois a solução não é difícil:

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

Isso fornece uma matriz de objetos de atributo, que possuem namee valuepropriedades:

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

Editar: para dar um passo adiante, você pode obter um dicionário iterando os atributos e preenchendo um objeto de dados:

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;
    }
});

Você pode acessar o valor de, por exemplo, data-my-value="2"como data.myValue;

jsfiddle.net/3KFYf/33

Editar: se você deseja definir atributos de dados em seu elemento programaticamente a partir de um objeto, você pode:

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

EDIT: Se você estiver usando babel ou TypeScript, ou codificando apenas para navegadores es6, este é um bom lugar para usar as funções de seta es6 e diminuir um pouco o código:

var a = [].filter.call(el.attributes, at => /^data-/.test(at.name));
gilly3
fonte
Boa resposta! A primeira linha não substitui o -caractere como o jQuery, mas a resposta editada.
Timo Huovinen
@ gilly3 Seu jsfiddle parece não funcionar. Você pode dar uma olhada?
Ethan
1
@ Ethan - Fixed. Obrigado por me avisar. Eu estava usando o beautify.js do jsbeautifier.org para imprimir bastante o JSON. Aparentemente, esse link está quebrado agora. Mas, isso era um exagero uma vez JSON.stringify()tem JSON formatação construído dentro.
gilly3
@ gilly3 Fantástico, sua rotina é ótima para buscar tudo. Seria fácil adicionar dados de busca para uma determinada chave e atualizar dados modificados para a (s) chave (s)? Se você pode compartilhar, isso seria ótimo.
21715 Ethan
@Ethan - Para obter um único valor, não overthink: el.getAttribute("data-foo"). Atualizei minha resposta para mostrar como você pode definir atributos de dados com base em um objeto.
gilly3
22

Dê uma olhada aqui :

Se o navegador também suportar a API JavaScript HTML5, você poderá obter os dados com:

var attributes = element.dataset

ou

var cat = element.dataset.cat

Ah, mas eu também li:

Infelizmente, a nova propriedade do conjunto de dados ainda não foi implementada em nenhum navegador; portanto, é melhor usá-lo getAttributee setAttributeconforme demonstrado anteriormente.

É a partir de maio de 2010.


Se você usar o jQuery de qualquer maneira, poderá dar uma olhada no plug- in customdata . Eu não tenho experiência com isso, no entanto.

Felix Kling
fonte
Também de interesse, de ppk: quirksmode.org/dom/w3c_core.html#attributes
Pointy
Obrigado Felix. Eu me deparei com dados personalizados. Infelizmente, ele não faz o que eu preciso - ou seja, obtém todos os dados, pois as chaves não são conhecidas antecipadamente.
Shawn Chin
@lsc: A documentação diz que isso $("#my").customdata()deve lhe dar {method: "delete", remote: "true"}... então deve funcionar.
Felix Kling
Usar um plugin jQuery separado parece um exagero, mas olhar para a fonte de dados customizados me ajudou a consertar minhas próprias soluções! Aceitará sua resposta e atualizará Q com a função de trabalho.
Shawn Chin
1
@lsc: Totalmente bom, as soluções integradas devem sempre ser preferidas. Infelizmente, eu não estou atualizado com o mais recente desenvolvimento do jQuery;) Estou me perguntando por que alguém me rejeitou ...
Felix Kling
5

Conforme mencionado acima, os navegadores modernos têm a API The HTMLElement.dataset .
Essa API fornece um DOMStringMap e você pode recuperar a lista de data-*atributos simplesmente fazendo:

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

você também pode recuperar uma matriz com os data-nomes das chaves da propriedade, como

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

ou mapeie seus 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];});

e assim você pode iterá-los e usá-los sem a necessidade de filtrar todos os atributos do elemento, como precisávamos fazer antes .

Sergio
fonte
3

ou converta gilly3a excelente resposta de um 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;
}

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

PHPst
fonte
2

Você deve obter os dados através dos atributos do conjunto de dados

var data = element.dataset;

conjunto de dados é uma ferramenta útil para obter atributo de dados

lychi
fonte
IIRC, this.datasetainda não funciona em todos os navegadores. No entanto, existe um plugin jquery que fornece esse recurso.
Shawn Chin
1

Você pode simplesmente iterar sobre os atributos de dados, como qualquer outro objeto, para obter chaves e valores. Veja como fazer isso $.each:

    $.each($('#myEl').data(), function(key, value) {
        console.log(key);
        console.log(value);
    });
Andrew
fonte
1

Eu uso aninhado cada um - para mim, essa é a solução mais fácil (fácil de controlar / alterar "o que você faz com os valores - no meu exemplo, output data-attribute as ul-list) (Jquery Code)

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
fonte
0

Uma maneira de encontrar todos os atributos de dados é usando element.attributes. Usando .attributes, você pode percorrer todos os atributos do elemento, filtrando os itens que incluem a string "data-".

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
fonte