Datenattribut kann nicht mit der jQuery Data () - API festgelegt werden

131

Ich habe das folgende Feld in einer MVC-Ansicht:

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

In einer separaten js-Datei möchte ich das data-helptextAttribut auf einen Zeichenfolgenwert setzen. Hier ist mein Code:

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

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

Der alert()Anruf funktioniert einwandfrei, er zeigt den Text "Alter Text" in einem Warndialog an. Der Aufruf zum Setzen des data-helptextAttributs auf "Testing 123" funktioniert jedoch nicht. "Alter Text" ist immer noch der aktuelle Wert des Attributs.

Benutze ich den Aufruf von data () falsch? Ich habe dies im Internet nachgeschlagen und kann nicht sehen, was ich falsch mache.

Hier ist das HTML-Markup:

<input data-helptext="Old Text" id="Course_Title" name="Course.Title" type="text" value="" />
Jason Evans
quelle
Der Code sieht OK aus. Kein Problem in dieser Demo . Welche Version von jQuery verwenden Sie?
Andyb
Ich verwende 1.5.1, das mit der ASP NET MVC-Projektvorlage geliefert wurde. Könnte es sein, dass ich jQuery aktualisieren muss?
Jason Evans
OK, dann ist es nicht die Version von jQuery. Ich dachte, es könnte eine wirklich alte Version sein. Die data () API, die Sie verwenden, wurde in v1.2.3
andyb
Könnten Sie bitte das Markup hinzufügen? Verwenden Sie ein benutzerdefiniertes HTML5- data-Attribut?
Andyb
Wie beobachten Sie den Wert? jQuery behält den Wert nicht im DOM bei, obwohl er korrekt aktualisiert wird. Siehe meine Antwort unten für einen Test und eine Erklärung
andyb

Antworten:

239

Es wird in der .data()Dokumentation erwähnt

Die Datenattribute werden beim ersten Zugriff auf die Dateneigenschaft abgerufen und dann nicht mehr abgerufen oder mutiert (alle Datenwerte werden dann intern in jQuery gespeichert).

Dies wurde auch behandelt unter Warum aktualisieren Änderungen an jQuery $ .fn.data () nicht die entsprechenden HTML 5-Daten- * -Attribute?

Die Demo zu meiner ursprünglichen Antwort unten scheint nicht mehr zu funktionieren.

Antwort aktualisiert

Wieder aus der .data()Dokumentation

Die Behandlung von Attributen mit eingebetteten Bindestrichen wurde in jQuery 1.6 geändert, um der W3C-HTML5-Spezifikation zu entsprechen.

Also für <div data-role="page"></div>folgendes gilt$('div').data('role') === 'page'

Ich bin mir ziemlich sicher, dass das $('div').data('data-role')in der Vergangenheit funktioniert hat, aber das scheint nicht mehr der Fall zu sein. Ich habe eher ein besseres Schaufenster , welche Protokolle in HTML erstellt , als wenn die Konsole zu öffnen und ein weiteres Beispiel für den Multi-Bindestrich hinzugefügt camelcase Daten- Attribute Konvertierung.

Aktualisierte Demo (25.07.2015)

Siehe auch 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();

Ältere Demo


Ursprüngliche Antwort

Für diesen HTML:

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

und dieses JavaScript (mit 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;
});

Siehe Demo

Wenn Sie die Chrome DevTools- Konsole zum Überprüfen des DOM verwenden, wird der in der Konsole angezeigte Wert $('#foo').data('helptext', 'Testing 123'); nicht aktualisiert , jedoch.$('#foo').attr('data-helptext', 'Testing 123');

andyb
quelle
1
Ich
bin
Was ist dann der Sinn von jQuery, mit dem Sie ein zweites Argument einbringen können? So aktualisieren Sie den im js-Variablencache gespeicherten Wert?
Ahnbizcad
@gwho Ich bin nicht sicher, ob ich Ihre Frage vollständig verstehe, aber ich gehe davon aus, dass Sie sich mit jQuery 1.6.2 auf die ursprüngliche Antwort von 2011 beziehen. Wenn ja, dann die. data('key', 'value')Verfahren macht den Wert im Cache jQuery aktualisieren , aber aus Leistungsgründen (I DOM - Mutation erraten) das DOM selbst nicht aktualisiert wird.
Andy
2
Wenn Sie also das DOM aktualisieren möchten, müssen Sie dies .attr('key','value')unabhängig davon tun, ob Sie es getan haben .data('key', 'value')oder nicht, oder? Das scheint mir überflüssig zu sein, und ich habe Probleme, mir ein Szenario vorzustellen, in dem Sie in das zwischengespeicherte DOM schreiben möchten, aber nicht in das echte DOM. Vielleicht verstehe ich den jQuery-Cache nicht; Würde ein Besucher also alle Dinge sehen, .data()die sich auf seinem Bildschirm ändern, oder nicht?
Ahnbizcad
1
Es geht also nicht nur um Leistung. Sie können nicht verglichen werden. Sie haben völlig unterschiedliche Zwecke und ändern unterschiedliche "Versionen" des DOM. Zurück zur Frage: Was bringt es, .data () zu verwenden, wenn Sie .attr () ausführen müssen, um das ACUTAL DOM zu ändern? scheint überflüssig.
Ahnbizcad
34

Ich hatte ernsthafte Probleme mit

.data('property', value);

Das data-propertyAttribut wurde nicht festgelegt .

Begonnen mit jQuery's .attr():

Rufen Sie den Wert eines Attributs für das erste Element in der Gruppe der übereinstimmenden Elemente ab oder legen Sie ein oder mehrere Attribute für jedes übereinstimmende Element fest.

.attr('property', value)

um den Wert einzustellen und

.attr('property')

um den Wert abzurufen.

Jetzt funktioniert es einfach!

Leniel Maccaferri
quelle
1
Für mich war es möglich, die Dateneigenschaft mit data () zu ändern, aber ich habe in Entwicklertools festgestellt, dass die Änderung nicht angezeigt wurde. Aus diesem Grund habe ich mich für attr () entschieden
drooh
8

@ andybs akzeptierte Antwort hat einen kleinen Fehler. Weiter zu meinem Kommentar zu seinem Beitrag oben ...

Für diesen HTML:

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

Sie müssen auf das Attribut wie folgt zugreifen:

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

aber die Datenmethode wie folgt:

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

Der obige Fix für die .data () -Methode verhindert "undefiniert" und der Datenwert wird aktualisiert (während der HTML-Code dies nicht tut).

Der Sinn des Attributs "Daten" besteht darin, einen Wert mit dem Element zu binden (oder zu "verknüpfen"). Sehr ähnlich dem onclick="alert('do_something')"Attribut, das eine Aktion an das Element bindet ... der Text ist nutzlos. Sie möchten nur, dass die Aktion funktioniert, wenn sie auf das Element klicken.

Sobald die Daten oder Aktionen an das Element gebunden sind, * muss der HTML-Code normalerweise nicht mehr aktualisiert werden, sondern nur die Daten oder Methoden, da dies von Ihrer Anwendung (JavaScript) verwendet wird. In Bezug auf die Leistung verstehe ich nicht, warum Sie sowieso auch den HTML-Code aktualisieren möchten. Niemand sieht das HTML-Attribut (außer in Firebug oder anderen Konsolen).

Eine Möglichkeit, wie Sie darüber nachdenken möchten: Der HTML-Code (zusammen mit den Attributen) ist nur Text. Die von JavaScript verwendeten Daten, Funktionen, Objekte usw. befinden sich auf einer separaten Ebene. Nur wenn JavaScript dazu aufgefordert wird, wird der HTML-Text gelesen oder aktualisiert, aber alle Daten und Funktionen, die Sie mit JavaScript erstellen, verhalten sich völlig getrennt von den HTML-Texten / -Attributen, die Sie in Ihrer Firebug-Konsole (oder einer anderen Konsole) sehen.

* Ich lege normalerweise Wert darauf , dass Sie den HTML-Code möglicherweise aktualisieren müssen, wenn Sie einen Fall haben, in dem Sie HTML beibehalten und exportieren müssen (z. B. eine Art Textformat- / datensensibler Texteditor), in dem der HTML-Code frisch auf eine andere Seite geladen wird auch.

Frank Forte
quelle
Vielen Dank. Dies half unter anderem bei falschen Antworten mit falschen Beispielen! Das dataIn attr('data-helptext'macht den Unterschied, wo die akzeptierte Antwort und die mit vielen Stimmen nicht funktionieren. +1
Aleks
6

Ist mir auch passiert. Es stellt sich heraus, dass

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

gibt Ihnen ein nicht beschreibbares Objekt. Ich habe es gelöst mit:

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

Und von da an datawar es ein standardmäßiges, beschreibbares JS-Objekt.

Nico
quelle
Wie schreibst du dann darauf?
Arbeitet für einen
Sorry Thom, ich weiß nicht was du meinst ... Nachdem $.extend...du das getan hast , kannst du verwenden datawie du willst: data.name = 'Nico'; data.questionSolved = true;und console.log(data)wirst diese neu hinzugefügten Eigenschaften anzeigen
Nico
3

Um ein Zitat zu zitieren:

Die Datenattribute werden beim ersten Zugriff auf die Dateneigenschaft abgerufen und dann nicht mehr abgerufen oder mutiert (alle Datenwerte werden dann intern in jQuery gespeichert).

.data() - jQuery-Dokumentation

Beachten Sie, dass diese (ehrlich gesagt ungerade ) Einschränkung nur der Verwendung von vorenthalten wird .data().

Die Lösung? Verwenden Sie .attrstattdessen.

Natürlich fühlen sich einige von Ihnen möglicherweise unwohl, wenn sie die dedizierte Methode nicht anwenden. Stellen Sie sich das folgende Szenario vor:

  • Der 'Standard' wird aktualisiert, sodass der Datenteil der benutzerdefinierten Attribute nicht mehr benötigt / ersetzt wird

Gesunder Menschenverstand - Warum sollten sie ein bereits etabliertes Attribut so ändern ? Man stelle sich vor classBeginn umbenannt Gruppe und idzu Kennung . Das Internet würde kaputt gehen.

Und selbst dann hat Javascript selbst die Möglichkeit, dies zu beheben - und natürlich könnte REGEX (und eine Vielzahl ähnlicher Methoden) trotz seiner berüchtigten Inkompatibilität mit HTML Ihre Attribute schnell in diesen neu-mythischen "Standard" umbenennen.

TL; DR

alert($(targetField).attr("data-helptext"));
Super Cat
quelle
1

Wie bereits erwähnt, .data()legt die Methode weder den Wert des data-Attributs fest, noch liest sie aktualisierte Werte, wenn sich das data-Attribut ändert.

Meine Lösung bestand darin, jQuery mit einer .realData()Methode zu erweitern, die tatsächlich dem aktuellen Wert des Attributs entspricht:

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

HINWEIS: Sicher könnte man einfach nutzen .attr(), aber aus meiner Erfahrung, die meisten Entwickler (aka me) machen den Fehler sehen .attr()und .data()als austauschbar, und oft ein , ohne zu denken für die andere ersetzen. Es mag die meiste Zeit funktionieren, aber es ist eine großartige Möglichkeit, Fehler einzuführen, insbesondere wenn es um irgendeine Art von dynamischer Datenbindung geht. Durch die Verwendung .realData()kann ich das beabsichtigte Verhalten genauer beschreiben.

Yarin
quelle
0

Hatte das gleiche Problem. Da Sie weiterhin Daten mit der Methode .data () abrufen können, müssen Sie nur einen Weg finden, um in die Elemente zu schreiben. Dies ist die Hilfsmethode, die ich verwende. Wie die meisten Leute gesagt haben, müssen Sie .attr verwenden. Ich lasse es jedes _ durch ersetzen - wie ich weiß, macht es das. Mir sind keine anderen Zeichen bekannt, die es ersetzt ... aber das habe ich nicht untersucht.

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

EDIT: 01.05.2017

Ich habe festgestellt, dass es immer noch Fälle gibt, in denen Sie mit integrierten Methoden nicht die richtigen Daten abrufen konnten. Daher verwende ich jetzt Folgendes:

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
quelle