web-dev-qa-db-fra.com

jQuery .data () ne fonctionne pas, mais .attr () fonctionne

Pardonnez-moi de ne pas être plus spécifique à ce sujet. J'ai un bug si étrange. Une fois le document chargé, je boucle certains éléments qui ont à l’origine un data-itemname="" et je règle ces valeurs à l’aide de .attr("data-itemname", "someValue").

Problème: lorsque je ferai une boucle plus tard sur ces éléments, si je fais elem.data().itemname, je reçois "", mais si je fais elem.attr("data-itemname"), je reçois "someValue". C'est comme si le .data() getter de jQuery n'obtient que les éléments configurés initialement pour contenir une valeur, mais s'ils sont vides à l'origine et définis ultérieurement, alors .data() n'obtient pas la valeur ultérieurement.

J'ai essayé de recréer ce bogue mais je n'ai pas pu le faire.

Modifier

J'ai recréé le bug! http://jsbin.com/ihuhep/edit#javascript,html,live

En outre, des extraits du lien ci-dessus ...

JS:

var theaters = [
    { name: "theater A", theaterId: 5 },
    { name: "theater B", theaterId: 17 }
];

$("#theaters").html(
    $("#theaterTmpl").render(theaters)
);

// DOES NOT WORK - .data("name", "val") does NOT set the val
var theaterA = $("[data-theaterid='5']");
theaterA.find(".someLink").data("tofilllater", "theater5link"); // this does NOT set data-tofilllater
$(".someLink[data-tofilllater='theater5link']").html("changed link text"); // never gets changed

// WORKS - .attr("data-name", "val") DOES set val
var theaterB = $("[data-theaterid='17']");
theaterB.find(".someLink").attr("data-tofilllater", "theater17link"); // this does set data-tofilllater
$(".someLink[data-tofilllater='theater17link']").html("changed link text");

HTML:

<body>
    <div id="theaters"></div>
</body>

<script id="theaterTmpl" type="text/x-jquery-tmpl">
    <div class="theater" data-theaterid="{{=theaterId}}">
        <h2>{{=name}}</h2>
        <a href="#" class="someLink" data-tofilllater="">need to change this text</a>
    </div>
</script>
92
Ian Davis

J'ai rencontré un "bogue" similaire il y a quelques jours lorsque j'ai utilisé .data() et .attr('data-name') pour les attributs de données HTML5. 

Le comportement que vous décrivez n'est pas un bug, mais est voulu.

L'appel .data() est spécial. Non seulement il récupère les attributs de données HTML5, il tente également d'évaluer/d'analyser les attributs. Donc, avec un attribut tel que data-myjson='{"hello":"world"}' lorsqu’il est récupéré via .data(), retournera une Object tandis que l’extraction via .attr() renverra une chaîne. Voir exemple jsfiddle.

Étant donné que .data() effectue un traitement supplémentaire, jQuery stocke les résultats de l'évaluation d'attribut dans $.cache. Après tout, une fois qu'un attribut de données a été évalué, il serait inutile de réévaluer chaque appel .data(), d'autant plus que les variables de données peuvent contenir des chaînes JSON complexes. 

J'ai dit tout cela pour dire ceci: Après avoir récupéré un attribut via .data(), aucune modification apportée par .attr('data-myvar', '') ne sera vue par les prochains appels .data().Testez ceci sur jsfiddle.

Pour éviter ce problème ne mélangez pas les appels .data et .attr(). Utilisez l'un ou l'autre.

179
leepowers

Ceci est le résultat d'un malentendu: data N'EST PAS un accesseur pour data-* attributs. C'est à la fois plus et moins que cela.

data est un accesseur pour le cache de données de jQuery sur l'élément. Ce cache est initialisé à partir d'attributs data-* s'il y en a, mais data ne jamais écrit dans les attributs, ni la modification de l'attribut ne modifie le cache de données après l'initialisation.

data masse également ce qu'il trouve de différentes manières, en devinant les types de données, en rendant data("foo") sur un élément avec data-foo="1" un nombre, pas une chaîne, ou même en analysant des éléments au format JSON s'ils semblent corrects:

console.log(typeof $("[data-foo]").data("foo"));
console.log(typeof $("[data-bar]").data("bar"));
<div data-foo="1"></div>
<div data-bar='{"answer":42}'></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Si vous souhaitez utiliser les attributs (en les lisant et en les définissant), utilisez attr , pas data . attrest un accesseur pour les attributs.

6
T.J. Crowder

C'est parce que le nom de l'attribut est data-itemname. Vous ne pouvez pas utiliser - dans la notation abrégée obj.attribute (obj.data-itemname serait interprété comme "obj.data moins itemname").

5
Marc B

Pourquoi n'utilisez-vous pas .data() partout?

Vous pouvez également déclarer les valeurs par défaut en ligne sur le code HTML, ce qui convient également.

<span data-code="pony">text</span>

et

$("span").data("code") == "pony" // true

si vous voulez le changer vous faites juste

$("span").data("code", "not-a-pony");

et pour l'enlever complètement, vous pouvez invoquer

$("span").removeData("code");

vous devriez vraiment essayer d'éviter d'utiliser .attr("data-*"), je ne sais pas pourquoi vous voudriez le faire de toute façon.

4
bevacqua

.attr("data-itemname", "someValue") modifie le DOM.

.data("itemname", "someValue") modifie le cache jQuery.

Pour que cela fonctionne en Javascript suivant et en plus en CSS, vous devez définir les deux.

theaterA.find(".someLink").attr("data-itemname", "someValue");
theaterA.find(".someLink").data("itemname", "someValue");
3
Elmar Höfinghoff

Vous devez définir les données à l'aide de .data('itemname', 'someValue');. L'utilisation de .attr() pour définir les attributs de données ne fonctionnera pas: http://jsfiddle.net/ThiefMaster/YHsKx/

Cependant, vous pouvez fournir des valeurs en ligne en utilisant par exemple. <div data-key="value"> dans le balisage.

1
ThiefMaster

Je peux voir que cela a entraîné une certaine division sur la façon d'aborder le paramètre d'attribut de données.

J'ai moi aussi rencontré ce problème et j'ai constaté que le problème semblait être simplement le formatage du nom d'attribut de données

D'après mon expérience, vous devriez éviter d'utiliser des traits d'union dans la variable de données (le nom de variable qui vient après "data-").

Cela n'a pas fonctionné pour moi:

[Balisage]

<div class="list" id="myElement1" data-item-order="some-value"> .... </div>

[jQuery]

jQuery("#myElement1").data("item-order", "my-new-value");

Mais ce qui suit a bien fonctionné! :):

(J'utilise un trait de soulignement au lieu d'un trait d'union si nécessaire)

[Balisage]

<div class="list" id="myElement1" data-item_order="some-value"> .... </div>

[jQuery]

jQuery("#myElement1").data("item_order", "my-new-value");

J'espère que ça aide . Bravo à tous!

0
mmmdearte