Impossible de définir l'attribut de données à l'aide de l'API jQuery Data ()

131

J'ai le champ suivant sur une vue MVC:

@Html.TextBoxFor(model => model.Course.Title, new { data_helptext = "Old Text" })</span>

Dans un fichier js séparé, je souhaite définir l' data-helptextattribut sur une valeur de chaîne. Voici mon code:

alert($(targetField).data("helptext"));

$(targetField).data("helptext", "Testing 123");

L' alert()appel fonctionne correctement, il affiche le texte "Ancien texte" dans une boîte de dialogue d'alerte. Cependant, l'appel pour définir l' data-helptextattribut sur "Testing 123" ne fonctionne pas. "Ancien texte" est toujours la valeur actuelle de l'attribut.

Est-ce que j'utilise l'appel aux données () de manière incorrecte? J'ai recherché cela sur le Web et je ne vois pas ce que je fais de mal.

Voici le balisage HTML:

<input data-helptext="Old Text" id="Course_Title" name="Course.Title" type="text" value="" />
Jason Evans
la source
Le code semble correct. Aucun problème dans cette démo . Quelle version de jQuery utilisez-vous?
andyb
J'utilise la version 1.5.1 fournie avec le modèle de projet ASP NET MVC. Se pourrait-il que je doive mettre à jour jQuery?
Jason Evans
OK, ce n'est pas la version de jQuery alors. Je pensais que c'était peut-être une version vraiment ancienne. L'API data () que vous utilisez a été ajoutée dans la v1.2.3
andyb
Pourriez-vous ajouter le balisage s'il vous plaît? Utilisez-vous un data-attribut HTML5 personnalisé ?
andyb
Comment observez-vous la valeur? jQuery ne conserve pas la valeur dans le DOM, bien qu'il la mette à jour correctement. Voir ma réponse ci-dessous pour un test et une explication
andyb

Réponses:

239

Il est mentionné dans la .data()documentation

Les attributs de données sont extraits lors du premier accès à la propriété de données, puis ne sont plus accédés ou mutés (toutes les valeurs de données sont ensuite stockées en interne dans jQuery)

Cela a également été abordé dans Pourquoi les modifications apportées à jQuery $ .fn.data () ne mettent-elles pas à jour les attributs html 5 data- * correspondants?

La démo de ma réponse originale ci-dessous ne semble plus fonctionner.

Réponse mise à jour

Encore une fois, à partir de la .data()documentation

Le traitement des attributs avec des tirets incorporés a été modifié dans jQuery 1.6 pour se conformer à la spécification HTML5 du W3C.

Donc pour <div data-role="page"></div>ce qui suit est vrai$('div').data('role') === 'page'

Je suis assez sûr que cela a $('div').data('data-role')fonctionné dans le passé, mais cela ne semble plus être le cas. J'ai créé une meilleure vitrine qui se connecte au HTML plutôt que d'avoir à ouvrir la console et j'ai ajouté un exemple supplémentaire de conversion des attributs de données multi-tirets en camelCase .

Démo mise à jour (25/07/2015)

Voir également jQuery Data vs Attr?

HTML

<div id="changeMe" data-key="luke" data-another-key="vader"></div>
<a href="#" id="changeData"></a>
<table id="log">
    <tr><th>Setter</th><th>Getter</th><th>Result of calling getter</th><th>Notes</th></tr>
</table>

JavaScript (jQuery 1.6.2+)

var $changeMe = $('#changeMe');
var $log = $('#log');

var logger;
(logger = function(setter, getter, note) {
    note = note || '';
    eval('$changeMe' + setter);
    var result = eval('$changeMe' + getter);
    $log.append('<tr><td><code>' + setter + '</code></td><td><code>' + getter + '</code></td><td>' + result + '</td><td>' + note + '</td></tr>');
})('', ".data('key')", "Initial value");

$('#changeData').click(function() {
    // set data-key to new value
    logger(".data('key', 'leia')", ".data('key')", "expect leia on jQuery node object but DOM stays as luke");
    // try and set data-key via .attr and get via some methods
    logger(".attr('data-key', 'yoda')", ".data('key')", "expect leia (still) on jQuery object but DOM now yoda");
    logger("", ".attr('key')", "expect undefined (no attr <code>key</code>)");
    logger("", ".attr('data-key')", "expect yoda in DOM and on jQuery object");

    // bonus points
    logger('', ".data('data-key')", "expect undefined (cannot get via this method)");
    logger(".data('anotherKey')", ".data('anotherKey')", "jQuery 1.6+ get multi hyphen <code>data-another-key</code>");
    logger(".data('another-key')", ".data('another-key')", "jQuery < 1.6 get multi hyphen <code>data-another-key</code> (also supported in jQuery 1.6+)");

    return false;
});

$('#changeData').click();

Démo plus ancienne


Réponse originale

Pour ce HTML:

<div id="foo" data-helptext="bar"></div>
<a href="#" id="changeData">change data value</a>

et ce JavaScript (avec jQuery 1.6.2)

console.log($('#foo').data('helptext'));

$('#changeData').click(function() {
    $('#foo').data('helptext', 'Testing 123');
//  $('#foo').attr('data-helptext', 'Testing 123');
    console.log($('#foo').data('data-helptext'));
    return false;
});

Voir la démo

En utilisant la console Chrome DevTools pour inspecter le DOM, le ne met pas à jour la valeur comme indiqué dans la console, mais le fait.$('#foo').data('helptext', 'Testing 123'); $('#foo').attr('data-helptext', 'Testing 123');

Andyb
la source
1
Je ne sais pas ce qui a changé, mais votre démo de violon revient indéfinie dans la console Chrome
Manubkk
Alors, quel est l'intérêt de jQuery vous permettant de mettre un deuxième argument? Pour mettre à jour la valeur stockée dans le cache de variables js?
ahnbizcad
@gwho Je ne suis pas sûr de bien comprendre votre question, mais je suppose que vous faites référence à la réponse originale de 2011 en utilisant jQuery 1.6.2. Si tel est le cas, alors le. data('key', 'value')méthode fait mettre à jour la valeur dans le cache jQuery , mais pour des raisons de performance (je suppose que la mutation DOM) le DOM lui - même est pas mis à jour.
andyb
2
donc si vous souhaitez mettre à jour le DOM, vous devez le faire, .attr('key','value')que vous l'ayez fait .data('key', 'value')ou non, non? Cela me semble redondant, et j'ai du mal à imaginer un scénario où vous voudriez écrire dans le DOM en cache, mais pas dans le vrai DOM. Peut-être que je ne comprends pas le cache jQuery; Alors un visiteur verrait-il toutes les choses qui .data()modifient sur son écran, ou non?
ahnbizcad
1
Ce n'est donc pas seulement une question de performance; ils ne peuvent pas être comparés. Ils ont des objectifs complètement différents et ils changent différentes «versions» du DOM. Revenons donc à la question: quel est l'intérêt d'utiliser .data () si vous devez faire .attr () pour changer le DOM ACUTAL? semble redondant.
ahnbizcad
34

J'avais de sérieux problèmes avec

.data('property', value);

Il ne définissait pas l' data-propertyattribut.

Commencé à utiliser jQuery .attr():

Obtenez la valeur d'un attribut pour le premier élément de l'ensemble des éléments correspondants ou définissez un ou plusieurs attributs pour chaque élément correspondant.

.attr('property', value)

pour définir la valeur et

.attr('property')

pour récupérer la valeur.

Maintenant, ça marche!

Leniel Maccaferri
la source
1
Pour moi, j'ai pu modifier la propriété de données avec data () mais j'ai remarqué que dans les outils de développement, il ne montrait pas le changement, donc pour cette raison, je suis allé avec attr ()
drooh
8

La réponse acceptée par @ andyb a un petit bogue. Suite à mon commentaire sur son post ci-dessus ...

Pour ce HTML:

<div id="foo" data-helptext="bar"></div>
<a href="#" id="changeData">change data value</a>

Vous devez accéder à l'attribut comme ceci:

$('#foo').attr('data-helptext', 'Testing 123');

mais la méthode de données comme celle-ci:

$('#foo').data('helptext', 'Testing 123');

Le correctif ci-dessus pour la méthode .data () empêchera "indéfini" et la valeur des données sera mise à jour (alors que le HTML ne le sera pas)

Le but de l'attribut "data" est de lier (ou de "lier") une valeur à l'élément. Très similaire à l' onclick="alert('do_something')"attribut, qui lie une action à l'élément ... le texte est inutile, vous voulez juste que l'action fonctionne quand ils cliquent sur l'élément.

Une fois que les données ou l'action sont liées à l'élément, il n'est généralement * pas nécessaire de mettre à jour le HTML, uniquement les données ou la méthode, car c'est ce que votre application (JavaScript) utiliserait. En termes de performances, je ne vois pas pourquoi vous voudriez également mettre à jour le HTML de toute façon, personne ne voit l'attribut html (sauf dans Firebug ou d'autres consoles).

Vous voudrez peut-être y penser: le HTML (ainsi que les attributs) ne sont que du texte. Les données, fonctions, objets, etc. utilisés par JavaScript existent sur un plan séparé. Ce n'est que lorsque JavaScript est invité à le faire qu'il lira ou mettra à jour le texte HTML, mais toutes les données et fonctionnalités que vous créez avec JavaScript agissent de manière totalement distincte du texte / attributs HTML que vous voyez dans votre console Firebug (ou autre).

* Je mets l'accent sur généralement parce que si vous avez un cas où vous avez besoin de préserver et d'exporter du HTML (par exemple une sorte d'éditeur de texte prenant en charge les micro-formats / données) où le HTML se chargera à nouveau sur une autre page, alors peut-être que vous avez besoin du HTML mis à jour aussi.

Frank Forte
la source
Merci. Cela a aidé, parmi toutes les autres réponses avec des exemples incorrects! Le datain attr('data-helptext'fait la différence, là où la réponse acceptée et celles avec beaucoup de votes ne fonctionnent pas. +1
Aleks
6

Cela m'est arrivé de la même manière. Il se trouve que

var data = $("#myObject").data();

vous donne un objet non inscriptible. Je l'ai résolu en utilisant:

var data = $.extend({}, $("#myObject").data());

Et à partir de là, dataétait un objet JS standard et inscriptible.

Nico
la source
Comment y écrivez-vous alors?
Works for a Living
Désolé Thom, je ne sais pas ce que vous voulez dire ... Après avoir fait $.extend..., vous pouvez utiliser datacomme vous s'il vous plaît:, data.name = 'Nico'; data.questionSolved = true;et console.log(data)affichera ces propriétés nouvellement ajoutées
Nico
3

Pour citer un devis:

Les attributs de données sont extraits lors du premier accès à la propriété de données, puis ne sont plus accédés ou mutés (toutes les valeurs de données sont ensuite stockées en interne dans jQuery).

.data() - Documentation jQuery

Notez que cette limitation (franchement étrange ) n'est réservée qu'à l'utilisation de .data().

La solution? Utilisez .attrplutôt.

Bien sûr, plusieurs d'entre vous peuvent se sentir mal à l'aise de ne pas utiliser sa méthode dédiée. Considérez le scénario suivant:

  • Le 'standard' est mis à jour afin que la partie données des attributs personnalisés ne soit plus requise / soit remplacée

Bon sens - Pourquoi changeraient-ils un attribut déjà établi comme ça? Imaginez simplement classcommencer à renommer le groupe et iden identifiant . Internet se briserait.

Et même dans ce cas, Javascript lui-même a la capacité de résoudre ce problème - Et bien sûr, malgré sa fameuse incompatibilité avec HTML, REGEX (et une variété de méthodes similaires) pourrait rapidement renommer vos attributs en ce nouveau `` standard '' mythique.

TL; DR

alert($(targetField).attr("data-helptext"));
Super chat
la source
1

Comme mentionné, la .data()méthode ne définira pas réellement la valeur de l' data-attribut, ni ne lira les valeurs mises à jour si ledata- attribut change.

Ma solution était d'étendre jQuery avec une .realData()méthode qui correspond en fait à la valeur actuelle de l'attribut:

// Alternative to .data() that updates data- attributes, and reads their current value.
(function($){
  $.fn.realData = function(name,value) {
      if (value === undefined) {
        return $(this).attr('data-'+name);
      } else {
        $(this).attr('data-'+name,value);
      }
  };
})(jQuery);

REMARQUE: Bien sûr, vous pouvez simplement utiliser .attr(), mais d'après mon expérience, la plupart des développeurs (aka moi) font l'erreur de visualiser .attr()et d' .data()être interchangeables, et souvent de substituer l'un à l'autre sans réfléchir. Cela peut fonctionner la plupart du temps, mais c'est un excellent moyen d'introduire des bogues, en particulier lorsqu'il s'agit de toute sorte de liaison de données dynamique. Donc, en utilisant .realData(), je peux être plus explicite sur le comportement prévu.

Yarin
la source
0

Avait le même problème. Puisque vous pouvez toujours obtenir des données en utilisant la méthode .data (), il vous suffit de trouver un moyen d'écrire dans les éléments. C'est la méthode d'aide que j'utilise. Comme la plupart des gens l'ont dit, vous devrez utiliser .attr. Je le fais remplacer tout _ par - comme je le sais. Je ne connais aucun autre personnage qu'il remplace ... mais je n'ai pas fait de recherche là-dessus.

function ExtendElementData(element, object){
    //element is what you want to set data on
    //object is a hash/js-object
    var keys = Object.keys(object);
    for (var i = 0; i < keys.length; i++){
        var key = keys[i];
        $(element).attr('data-'+key.replace("_", "-"), object[key]);
    }
}

MODIFIER: 01/05/2017

J'ai trouvé qu'il y avait encore des cas où vous ne pouviez pas obtenir les données correctes à l'aide de méthodes intégrées, donc ce que j'utilise maintenant est la suivante:

function setDomData(element, object){
    //object is a hash

    var keys = Object.keys(object);
    for (var i = 0; i < keys.length; i++){
        var key = keys[i];
        $(element).attr('data-'+key.replace("_", "-"), object[key]);
    }
};

function getDomData(element, key){
    var domObject = $(element).get(0);
    var attKeys = Object.keys(domObject.attributes);

    var values = null;
    if (key != null){
        values = $(element).attr('data-' + key);
    } else {
        values = {};

        var keys = [];
        for (var i = 0; i < attKeys.length; i++) {
            keys.push(domObject.attributes[attKeys[i]]);
        }

        for (var i = 0; i < keys.length; i++){
            if(!keys[i].match(/data-.*/)){
                values[keys[i]] = $(element).attr(keys[i]);
            }
        }
    }
    return values;
};
Matthew Pautzke
la source