Obtenir la liste des attributs data- * en utilisant javascript / jQuery

150

Étant donné un élément HTML arbitraire avec zéro ou plusieurs data-*attributs, comment récupérer une liste de paires clé-valeur pour les données.

Par exemple, étant donné ceci:

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

J'aimerais pouvoir récupérer par programme ceci:

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

En utilisant jQuery (v1.4.3), accéder aux bits de données individuels en utilisant $.data()est simple si les clés sont connues à l'avance, mais il n'est pas évident de savoir comment le faire avec des ensembles de données arbitraires.

Je recherche une solution jQuery «simple» s'il en existe une, mais cela ne me dérangerait pas une approche de niveau inférieur autrement. J'ai essayé d'analyser $('#prod').attributesmais mon manque de javascript-fu me laisse tomber.

mettre à jour

customdata fait ce dont j'ai besoin. Cependant, inclure un plugin jQuery juste pour une fraction de ses fonctionnalités semblait être une exagération.

Regarder la source m'a aidé à corriger mon propre code (et a amélioré mon javascript-fu).

Voici la solution que j'ai trouvée:

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

mise à jour 2

Comme démontré dans la réponse acceptée, la solution est triviale avec jQuery (> = 1.4.4). $('#prod').data()renverrait les données requises dict.

Shawn Chin
la source
Adressez-vous à "update 2", sachez que jquery data () utilise un cache interne et que les données (clé, valeur) ne se propagent pas au DOM, ce qui peut causer beaucoup de maux de tête. Aussi depuis jquery 3 data () convertit l'attribut data-some-thing = "test" en {someThing: "test"}
ETNyx

Réponses:

96

En fait, si vous travaillez avec jQuery, à partir de la version 1.4.31.4.4 (en raison du bogue mentionné dans les commentaires ci-dessous), les data-*attributs sont pris en charge via .data():

À partir de jQuery 1.4.3, les data- attributs HTML 5 seront automatiquement intégrés à l'objet de données de jQuery.

Notez que les chaînes sont laissées intactes tandis que les valeurs JavaScript sont converties à leur valeur associée (cela inclut les booléens, les nombres, les objets, les tableaux et null). Les data- attributs sont extraits lors du premier accès à la propriété data, puis ne sont plus accédés ou mutés (toutes les valeurs de données sont ensuite stockées en interne dans jQuery).

La jQuery.fn.datafonction retournera tous les data-attributs à l'intérieur d'un objet sous forme de paires clé-valeur, la clé étant la partie du nom d'attribut après data-et la valeur étant la valeur de cet attribut après avoir été convertie en suivant les règles énoncées ci-dessus.

J'ai également créé une démo simple si cela ne vous convainc pas: http://jsfiddle.net/yijiang/WVfSg/

Yi Jiang
la source
3
Non, cela est cassé dans 1.4.3 . Un minimum de 1.4.4 est explicitement requis.
Crescent Fresh
Merci. C'est exactement ce que je cherchais. J'avais déjà essayé cela avec la version 1.4.3 et je ne suis pas allé très loin. La démo de jsfiddle a également aidé.
Shawn Chin
2
@Isc: une chose qui est très ennuyeux est la tentative de jQuery à « désérialisation » la valeur trouvée dans l'attribut ( par exemple du maître : !jQuery.isNaN( data ) ? parseFloat( data ) : ...). J'avais des numéros de série de produits dans mon attribut, tels que data-serial="00071134"jQuery fusionné dans un nombre 71134, me forçant à revenir au moins élégant .attr('data-serial')(pas une option dans votre cas). J'ai vu des questions sur SO qui ont exprimé des frustrations similaires .data(), j'essaierai d'en creuser une ...
Crescent Fresh
@CrescentFresh: Bon point. Certainement quelque chose à quoi je devrais faire attention. Dans ma solution ( gist.github.com/701652 ) je reviens à l'analyse de node.attributes lorsque jQuery <1.4.3; avec ce problème à l'esprit, peut-être que je devrais simplement m'en tenir à l'analyse manuelle des attributs.
Shawn Chin
7
Gardez à l'esprit que $.data()renvoie également tout ce que vous avez stocké en utilisant $.data(key, value). Si vous voulez vraiment vérifier si quelque chose est réellement stocké dans le balisage en tant qu'attribut data- *, cette méthode n'est peut-être pas le meilleur choix.
Philip Walton
81

Une solution JavaScript pure devrait également être proposée, car la solution n'est pas difficile:

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

Cela donne un tableau d'objets attribut, qui ont nameet valuepropriétés:

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

Modifier: pour aller plus loin, vous pouvez obtenir un dictionnaire en itérant les attributs et en remplissant un objet de données:

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

Vous pouvez alors accéder à la valeur de, par exemple, data-my-value="2"comme data.myValue;

jsfiddle.net/3KFYf/33

Modifier: si vous souhaitez définir des attributs de données sur votre élément par programmation à partir d'un objet, vous pouvez:

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: Si vous utilisez babel ou TypeScript, ou codez uniquement pour les navigateurs es6, c'est un bon endroit pour utiliser les fonctions fléchées es6 et raccourcir un peu le code:

var a = [].filter.call(el.attributes, at => /^data-/.test(at.name));
gilly3
la source
Bonne réponse! La première ligne ne remplace pas le -caractère comme le fait jQuery mais la réponse modifiée le fait.
Timo Huovinen
@ gilly3 Votre jsfiddle ne semble pas fonctionner. Pouvez-vous jeter un oeil?
Ethan
1
@Ethan - Corrigé. Merci de me le faire savoir. J'avais utilisé beautify.js de jsbeautifier.org pour bien imprimer le JSON. Apparemment, ce lien est rompu maintenant. Mais c'était exagéré depuis le JSON.stringify()formatage JSON intégré.
gilly3
@ gilly3 Fantastique, votre routine est géniale pour tout récupérer. Serait-il facile d'ajouter des données d'extraction pour une clé donnée ainsi que de mettre à jour les données modifiées pour la ou les clés? Si vous pouvez partager, ce serait génial.
Ethan
@Ethan - Pour obtenir une seule valeur, n'overthink pas: el.getAttribute("data-foo"). J'ai mis à jour ma réponse pour montrer comment vous pouvez définir des attributs de données en fonction d'un objet.
gilly3
22

Regardez ici :

Si le navigateur prend également en charge l'API JavaScript HTML5, vous devriez pouvoir obtenir les données avec:

var attributes = element.dataset

ou

var cat = element.dataset.cat

Oh, mais j'ai aussi lu:

Malheureusement, la nouvelle propriété de l'ensemble de données n'a encore été implémentée dans aucun navigateur, il est donc préférable de l'utiliser en attendant getAttributeet setAttributecomme démontré précédemment.

C'est à partir de mai 2010.


Si vous utilisez quand même jQuery, vous voudrez peut-être jeter un coup d'œil au plugin customdata . Je n'ai cependant aucune expérience avec cela.

Félix Kling
la source
Aussi intéressant, de ppk: quirksmode.org/dom/w3c_core.html#attributes
Pointy
Merci Felix. J'étais tombé sur des données personnalisées. Malheureusement, il ne fait pas ce dont j'ai besoin - c'est-à-dire obtenir toutes les données étant donné que les clés ne sont pas connues à l'avance.
Shawn Chin du
@lsc: La documentation dit que cela $("#my").customdata()devrait vous donner {method: "delete", remote: "true"}... donc cela devrait fonctionner.
Felix Kling
Utiliser un plugin jQuery séparé semble exagéré, mais regarder la source des données personnalisées m'a aidé à corriger mes propres solutions! Acceptera votre réponse et mettra à jour Q avec la fonction de travail.
Shawn Chin
1
@lsc: Tout à fait bien, les solutions intégrées devraient toujours être préférées à imo. Malheureusement, je ne suis pas à jour avec le dernier développement de jQuery;) Je me demande juste pourquoi quelqu'un m'a voté contre ...
Felix Kling
5

Comme mentionné ci-dessus, les navigateurs modernes ont l' API The HTMLElement.dataset .
Cette API vous donne un DOMStringMap , et vous pouvez récupérer la liste des data-*attributs en faisant simplement:

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

vous pouvez également récupérer un tableau avec les data-noms de clé de la propriété comme

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

ou mappez ses valeurs par

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

et comme ça, vous pouvez les itérer et les utiliser sans avoir besoin de filtrer entre tous les attributs de l'élément comme nous devions le faire auparavant .

Sergio
la source
3

ou convertissez gilly3l'excellente réponse en une méthode 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;
}

En utilisant $('.foo').info():;

PHPst
la source
2

Vous devriez obtenir les données via les attributs de l'ensemble de données

var data = element.dataset;

dataset est un outil utile pour obtenir l'attribut de données

Lychi
la source
IIRC, this.datasetne fonctionne pas encore sur tous les navigateurs. Il existe cependant un plugin jquery qui fournit cette fonctionnalité.
Shawn Chin
1

Vous pouvez simplement parcourir les attributs de données comme n'importe quel autre objet pour obtenir des clés et des valeurs, voici comment le faire avec $.each:

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

J'utilise chacun imbriqué - pour moi, c'est la solution la plus simple (Facile à contrôler / modifier "ce que vous faites avec les valeurs - dans mon exemple, les attributs de données de sortie comme ul-list) (Code 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
la source
0

Une façon de trouver tous les attributs de données consiste à utiliser element.attributes. En utilisant .attributes, vous pouvez parcourir tous les attributs d'élément, en filtrant les éléments qui incluent la chaîne "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
la source