jQuery .data () funktioniert nicht, aber .attr ()

107

Verzeihen Sie mir, dass ich nicht genauer darauf eingegangen bin. Ich habe so einen seltsamen Fehler. Nachdem das Dokument geladen wurde, schleife ich einige Elemente, die ursprünglich vorhanden waren data-itemname="", und setze diese Werte mit .attr("data-itemname", "someValue").

Problem: Wenn ich diese Elemente später durchschleife elem.data().itemname, bekomme ich "", wenn ich es tue elem.attr("data-itemname"), aber wenn ich es tue , bekomme ich "someValue". Es ist, als würde der .data()Getter von jQuery nur Elemente abrufen, die anfänglich so eingestellt sind, dass sie einen bestimmten Wert enthalten. Wenn sie jedoch ursprünglich leer sind und später festgelegt werden, .data()wird der Wert später nicht mehr abgerufen.

Ich habe versucht, diesen Fehler wiederherzustellen, konnte es aber nicht.

Bearbeiten

Ich habe den Fehler neu erstellt! http://jsbin.com/ihuhep/edit#javascript,html,live

Auch Ausschnitte aus dem obigen Link ...

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>
Ian Davis
quelle
4
Versuchen Sie stärker, den Fehler neu zu erstellen :-)
dkamins
1
Ist es elem.data("itemname")nicht nicht elem.data().itemname?
Hogan
Mit elem.data (). itemname können Sie den Wert lesen, mit dem Sie den Wert NICHT festlegen können. (Ändert elem.data().itemname = somevalue;also nicht die zugrunde liegenden Daten.)
Hogan
@dkamins - Ich habe den Fehler neu erstellt, siehe bearbeitete Version.
Ian Davis

Antworten:

210

Ich bin vor einigen Tagen auf einen ähnlichen "Fehler" gestoßen, als ich mit .data()und .attr('data-name')für HTML5-Datenattribute gearbeitet habe.

Das Verhalten, das Sie beschreiben, ist kein Fehler, sondern beabsichtigt.

Der .data()Aufruf ist etwas Besonderes - er ruft nicht nur HTML5-Datenattribute ab, sondern versucht auch, die Attribute auszuwerten / zu analysieren. data-myjson='{"hello":"world"}'Wenn also ein Attribut wie beim Abrufen von über zurückgegeben .data()wird, wird Objectbeim Abrufen von über .attr()eine Zeichenfolge zurückgegeben. Siehe jsfiddle-Beispiel.

Da .data()bei der zusätzlichen Verarbeitung jQuery die Ergebnisse der Attributauswertung in speichert, ist es nach der Auswertung $.cacheeines Datenattributs verschwenderisch, bei jedem .data()Aufruf eine Neubewertung vorzunehmen - insbesondere, da Datenvariablen komplexe JSON-Zeichenfolgen enthalten können.

Ich habe das alles gesagt, um Folgendes zu sagen: Nach dem Abrufen eines Attributs über .data()Änderungen, die von vorgenommen wurden, .attr('data-myvar', '')werden nachfolgende .data()Aufrufe nicht mehr angezeigt. Testen Sie dies auf jsfiddle.

Um dieses Problem zu vermeiden, mischen Sie nicht .dataund .attr()rufen Sie nicht auf. Verwenden Sie den einen oder anderen.

Leepowers
quelle
Markieren Sie dies als Antwort. In diesem Test finden Sie alle möglichen Szenarien mit .data () vs. .attr (), einschließlich des Abrufens und Einstellens der einzelnen Szenarien und der Ausgabe der Länge der Selektoren nach deren Festlegung unter jsbin.com/acegef/edit# Javascript, HTML, live
Ian Davis
9
Zumindest erklärt dies das anscheinend nicht intuitive Verhalten ... obwohl dies zu kleineren Kopfschmerzen führen kann, wenn Bibliotheken von Drittanbietern verwendet werden, von denen einige nur data () verwenden und andere das tatsächliche Attribut ändern.
Haroldo_OK
1
@leepowers, "Nach dem Abrufen eines Attributs über .data () werden alle von .attr ('data-myvar', '') vorgenommenen Änderungen bei nachfolgenden .data () -Aufrufen nicht angezeigt", wenn dies wirklich auf den Cache zurückzuführen ist ( $ .cache), dann scheint es nicht gut zu sein! Anstatt sich blind auf den Cache zu verlassen, könnten nachfolgende .data () -Aufrufe einige grundlegende Überprüfungen () durchführen, z. B. ob der Wert jetzt leer ist oder die Zeichenfolgenlänge geändert wurde (Übereinstimmung des Caches mit dem aktuellen Wert)
minhajul
1
Es sollte beachtet werden, dass der Versuch, Daten ('id', val) festzulegen, auch fehlschlägt, wenn der Wert nicht früher abgerufen wurde ... großartiges Design der data () - Funktion durch Jquery.
andreszs
3
Es ist also der data () - Cache, der mich stundenlang hält. Kein Wunder, warum es immer noch den gleichen Wert zurückgibt, egal wie sehr ich seinen Wert ändere. Danke dafür.
Lynnell Emmanuel Neri
17

Dies ist das Ergebnis eines Missverständnisses: dataist KEIN Accessor für data-*Attribute . Es ist mehr und weniger.

dataist ein Accessor für den Datencache von jQuery für das Element. Das Cache wird initialisiert von data-*Attributen , wenn überhaupt vorhanden sind, aber datanie schreibt auf die Attribute, noch hat die Attributänderung nach der Initialisierung Ändern der Daten - Cache:

const div = $("[data-example]");
console.log('div.data("example"):', div.data("example"));
console.log('div.attr("data-example"):', div.attr("data-example"));
console.log('Using div.data("example", "updated")');
div.data("example", "updated");
console.log('div.data("example"):', div.data("example"));
console.log('div.attr("data-example"):', div.attr("data-example"));
<div data-example="initial value"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

dataMassiert auch das, was es findet, auf verschiedene Arten, errät Datentypen, erstellt data("answer")ein Element mit data-answer="42"einer Zahl, nicht einer Zeichenfolge, oder analysiert sogar Dinge als JSON, wenn sie wie JSON aussehen:

console.log(typeof $("[data-answer]").data("answer"));
console.log(typeof $("[data-json]").data("json"));
console.log(typeof $("[data-str]").data("str"));
<div data-answer="42"></div>
<div data-json='{"answer":42}'></div>
<div data-str="example"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Wenn Sie die Attribute verwenden möchten (sowohl zum Lesen als auch zum Festlegen), verwenden Sie attrnicht data. attr ist ein Accessor für Attribute.

TJ Crowder
quelle
6

Das liegt daran, dass das Attribut heißt data-itemname. Sie können nicht -in der Kurzschreibweise obj.attributeverwenden (obj.data-itemname wird als "obj.data minus itemname" interpretiert).

Marc B.
quelle
Sagt wer? Können Sie einen Link bereitstellen?
Jahrichie
6

.attr("data-itemname", "someValue") ändert das DOM.

.data("itemname", "someValue") Ändert den jQuery-Cache.

Damit dies in folgendem Javascript und zusätzlich in CSS funktioniert, müssen Sie beide festlegen.

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

Warum benutzt du nicht einfach .data()überall?

Sie können Standardwerte auch inline im HTML deklarieren, was ebenfalls in Ordnung ist.

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

und

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

Wenn Sie es ändern möchten, tun Sie es einfach

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

und um es insgesamt zu entfernen, könnten Sie aufrufen

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

Sie sollten wirklich versuchen, die Verwendung zu vermeiden .attr("data-*"). Ich weiß nicht, warum Sie dies sowieso tun möchten.

bevacqua
quelle
s / sollte / muss, .attr('data-*', ...)wird die Daten nicht sichtbar machen für.data()
ThiefMaster
Aber ist es nicht mit attr () so, wie Sie es ohne jQuery tun müssten ? (mit getAttribute tho)
Powerbuoy
Wenn Sie nicht über jQuery verfügen, verwenden Sie getAttribute()und setAttribute()-, sodass beide Methoden auf die tatsächlichen Attribute zugreifen und es wieder funktioniert. Oder Sie nutzen einfach die dataSetEigenschaft, die moderne Browser bieten.
ThiefMaster
1
Wählen Sie solche Elemente nicht aus. Es ist schrecklich ineffizient, da es jedes einzelne Element im Dokument durchlaufen muss . Verwenden Sie classstattdessen a, da Browser native Funktionen haben, um Elemente mit einer bestimmten Klasse abzurufen.
ThiefMaster
1
"555" sind jedoch Daten, daher sollte ich die data () - Logik verwenden. Wenn Sie diese Daten in den Klassennamen einfügen, werden Daten mit der Präsentation gemischt. andere Art und Weise, nehme ich an.
Ian Davis
1

Sie müssen die Daten mit einstellen .data('itemname', 'someValue');. Das Festlegen .attr()von Datenattributen funktioniert nicht: http://jsfiddle.net/ThiefMaster/YHsKx/

Sie können jedoch Inline-Werte bereitstellen, indem Sie z <div data-key="value">. B. im Markup verwenden.

DiebMaster
quelle
In der jsfiddle funktioniert get, nachdem Sie beide Sets ausgeführt haben. attr ("data-itemname", "value") funktioniert also. Ich benutze Chrome.
Ian Davis
@ Ian äh, nein. Der .data()Aufruf setzt das Attribut, während der .attr()Aufruf nichts tut.
Bevacqua
@IanDavis: Klicken Sie auf "set attr" und "get" gibt Ihnen ein leeres Objekt. Nur "Daten einstellen" funktioniert.
ThiefMaster
@Nico - genau das habe ich getan: (1) Klicken Sie auf die Schaltfläche "attr setzen" und dann (2) auf die Schaltfläche "get". Folgendes ist passiert: Es hat mich alarmiert, "{" test ":" meow "}". Daher hat .attr () das Attribut festgelegt. Andernfalls wäre die Warnung leer gewesen. Wenn Sie genau diese Schritte befolgen, erhalten Sie unterschiedliche Ergebnisse? Vielen Dank.
Ian Davis
1
@ThiefMaster - in theaterA in meinem bereitgestellten Code, bin ich nicht mit .attr()überhaupt, nur .data(), und die Länge des Wählers, $(".someLink[data-tofilllater='theater5link']")ist Null. Es ist also so, als müsste ich Folgendes verwenden .attr(): /
Ian Davis
0

Ich kann sehen, dass dies zu einer gewissen Aufteilung der Vorgehensweise bei der Einstellung der Datenattribute geführt hat.

Auch ich bin auf dieses Problem gestoßen und habe festgestellt, dass das Problem einfach in der Formatierung des Namens des Datenattributs zu liegen scheint .

Nach meiner Erfahrung sollten Sie die Verwendung von Bindestrichen in der Datenvariablen (dem Variablennamen nach " data- ") vermeiden .

Das hat bei mir nicht funktioniert:

[Markup]

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

[jQuery]

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

Aber das Folgende hat gut funktioniert! :):

(Ich verwende bei Bedarf einen Unterstrich anstelle eines Bindestrichs.)

[Markup]

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

[jQuery]

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

Ich hoffe, es hilft. Prost alle!

mmmdearte
quelle