jQuery .data () nie działa, ale .attr () tak

107

Wybaczcie, że nie jestem bardziej szczegółowy w tej sprawie. Mam taki dziwny błąd. Po załadowaniu dokumentu zapętlam niektóre elementy, które pierwotnie miały data-itemname="", i ustawiam te wartości za pomocą .attr("data-itemname", "someValue").

Problem: Kiedy później zapętlę te elementy, jeśli tak elem.data().itemname, otrzymam "", ale jeśli tak elem.attr("data-itemname"), otrzymam "someValue". To tak, jakby .data()getter jQuery pobierał tylko te elementy, które są początkowo ustawione tak, aby zawierały jakąś wartość, ale jeśli są pierwotnie puste, a później ustawione, później .data()nie pobiera wartości.

Próbowałem odtworzyć ten błąd, ale nie udało mi się.

Edytować

Odtworzyłem błąd! http://jsbin.com/ihuhep/edit#javascript,html,live

Ponadto fragmenty z powyższego linku ...

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
źródło
4
Spróbuj bardziej odtworzyć błąd :-)
dkamins,
1
Nie jest to elem.data("itemname")nie elem.data().itemname?
Hogan,
natomiast elem.data (). itemname pozwoli ci odczytać wartość, której NIE pozwoli ci ustawić wartości. (W ten elem.data().itemname = somevalue;sposób nie zmienia podstawowych danych.)
Hogan
@dkamins - odtworzyłem błąd, zobacz edytowaną wersję.
Ian Davis,

Odpowiedzi:

210

Kilka dni temu napotkałem podobny „błąd” podczas pracy z atrybutami danych HTML5 .data()i .attr('data-name')dla nich.

Zachowanie, które opisujesz, nie jest błędem, ale zostało zaprojektowane.

.data()Wezwanie jest wyjątkowy - to nie tylko pobierać dane atrybutów HTML5 to także próba oceny / analizowania atrybuty. Tak więc z atrybutem takim jak data-myjson='{"hello":"world"}'przy pobieraniu przez .data()zwróci Objectwhile pobieranie przez .attr()zwróci ciąg. Zobacz przykład jsfiddle.

Ponieważ .data()wymaga dodatkowego przetwarzania, jQuery przechowuje wyniki oceny atrybutów w $.cache- w końcu po ocenie atrybutu danych byłoby marnotrawstwem ponowne oszacowanie przy każdym .data()wywołaniu - zwłaszcza, że ​​zmienne danych mogą zawierać złożone ciągi JSON.

Powiedziałem to wszystko, aby powiedzieć, co następuje: po pobraniu atrybutu za pośrednictwem .data()jakichkolwiek zmian dokonanych przez .attr('data-myvar', '')nie będzie widoczny dla kolejnych .data()wywołań. Przetestuj to na jsfiddle.

Aby uniknąć tego problemu , nie mieszaj .datai nie .attr()dzwoni. Użyj jednego lub drugiego.

leepowers
źródło
oznaczając to jako odpowiedź. zobacz ten test dla wszystkich możliwych scenariuszy z .data () vs. .attr (), który obejmuje pobieranie i ustawianie ich przy użyciu każdego z nich oraz wyprowadzanie długości selektorów po ich ustawieniu, jsbin.com/acegef/edit# javascript, html, live
Ian Davis
9
Przynajmniej to wyjaśnia pozornie nieintuicyjne zachowanie ... chociaż może to powodować mniejsze bóle głowy, gdy ktoś korzysta z bibliotek innych firm, niektóre z nich używają tylko danych (), a inne zmieniają rzeczywisty atrybut.
Haroldo_OK,
1
@leepowers, „Po pobraniu atrybutu przez .data () wszelkie zmiany dokonane przez .attr ('data-myvar', '') nie będą widoczne dla kolejnych wywołań .data ()”, jeśli to naprawdę z powodu pamięci podręcznej ( $ .cache), to nie wydaje się dobre! zamiast ślepo polegać na pamięci podręcznej, kolejne wywołania .data () mogą wykonywać pewne podstawowe testy (), na przykład czy wartość jest teraz pusta lub zmieniła się długość ciągu (dopasowanie pamięci podręcznej do bieżącej wartości)
minhajul
1
Należy zauważyć, że próba ustawienia danych ('id', val) również kończy się niepowodzeniem, jeśli wartość nie została pobrana wcześniej ... świetny projekt funkcji data () przez Jquery.
andreszs
3
Więc to pamięć podręczna danych (), która trzyma mnie przez wiele godzin. Nic dziwnego, że bez względu na to, jak bardzo zmienię jego wartość, nadal zwraca tę samą wartość. Dzięki za to.
Lynnell Emmanuel Neri
17

Jest to wynikiem nieporozumienia: dataNIE jest akcesorium do data-*atrybutów . To zarówno więcej, jak i mniej.

datajest akcesorem dla pamięci podręcznej danych jQuery w elemencie. Ta pamięć podręczna jest inicjowana z data-*atrybutów, jeśli istnieją, ale datanigdy nie zapisuje do atrybutów, ani zmiana atrybutu nie zmienia pamięci podręcznej danych po inicjalizacji:

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>

datamasuje również to, co znajdzie na różne sposoby, zgadując typy danych, tworząc data("answer")element z data-answer="42"liczbą, a nie ciągiem, a nawet analizując rzeczy jako JSON, jeśli wyglądają jak JSON:

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>

Jeśli chcesz używać atrybutów (zarówno do czytania, jak i ustawiania), użyj attr, nie data. attr jest akcesorium do atrybutów.

TJ Crowder
źródło
6

To dlatego, że nazwa atrybutu to data-itemname. Nie można używać -w obj.attributenotacji skróconej (obj.data-itemname byłoby interpretowane jako „obj.data minus itemname”).

Marc B.
źródło
mówi kto? czy możesz podać link?
jahrichie
6

.attr("data-itemname", "someValue") modyfikuje DOM.

.data("itemname", "someValue") modyfikuje pamięć podręczną jQuery.

Aby to działało w Javascript i dodatkowo w CSS, musisz ustawić oba.

theaterA.find(".someLink").attr("data-itemname", "someValue");
theaterA.find(".someLink").data("itemname", "someValue");
Elmar Höfinghoff
źródło
4

Dlaczego nie używasz .data()wszędzie?

Możesz także zadeklarować domyślne wartości w kodzie HTML, co też jest w porządku.

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

i

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

jeśli chcesz to zmienić, po prostu to robisz

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

i aby go całkowicie usunąć, możesz przywołać

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

naprawdę powinieneś próbować i unikać używania .attr("data-*"), i tak nie wiem, dlaczego chcesz to zrobić.

bevacqua
źródło
s / powinien / musi, użycie .attr('data-*', ...)nie spowoduje, że dane będą widoczne dla.data()
ThiefMaster
Ale czy nie robisz tego z attr () tak, jak byś to robił bez jQuery? (z getAttribute tho)
powerbuoy
Jeśli nie masz jQuery, używasz getAttribute()isetAttribute() - więc obie metody będą dostęp do rzeczywistych atrybutów i będą działać ponownie. Lub po prostu skorzystaj z dataSetwłaściwości udostępnianej przez nowoczesne przeglądarki.
ThiefMaster
1
Nie wybieraj takich elementów. Jest okropnie nieefektywny, ponieważ będzie musiał iterować po każdym elemencie w dokumencie. Użyj classzamiast tego, ponieważ przeglądarki mają natywne funkcje do pobierania elementów z określoną klasą.
ThiefMaster
1
ale „555” to dane, więc powinienem używać logiki data (). umieszczenie tych danych w nazwie klasy jest mieszaniem danych z prezentacją. chyba w inny sposób.
Ian Davis
1

Musisz ustawić dane za pomocą .data('itemname', 'someValue');. Używanie .attr()do ustawiania atrybutów danych nie zadziała: http://jsfiddle.net/ThiefMaster/YHsKx/

Możesz jednak podać wartości wbudowane, używając np. <div data-key="value">W znacznikach.

ThiefMaster
źródło
w jsfiddle get działa po wykonaniu obu zestawów. więc wykonanie attr ("data-itemname", "value") DZIAŁA. Używam Chrome.
Ian Davis,
@Ian uh, nie. .data()wezwanie ustawia atrybut, natomiast .attr()rozmowa nic nie robi.
bevacqua
@IanDavis: Kliknij "set attr", a "get" da ci pusty obiekt. Działa tylko „zestaw danych”.
ThiefMaster
@Nico - dokładnie to zrobiłem: (1) kliknij przycisk „ustaw atrybut”, a następnie (2) kliknij przycisk „pobierz”. oto co się stało: zaalarmowało mnie, "{" test ":" meow "}". więc .attr () ustawia atrybut. w przeciwnym razie alert byłby pusty. Jeśli wykonasz te dokładne kroki, czy uzyskasz inne wyniki? dzięki.
Ian Davis,
1
@ThiefMaster - w kodzie TheaterA w podanym przeze mnie kodzie w ogóle nie używam .attr(), tylko .data(), a długość selektora $(".someLink[data-tofilllater='theater5link']")wynosi zero. więc to tak, jakbym musiał użyć .attr(): /
Ian Davis
0

Widzę, że spowodowało to podział na sposób podejścia do ustawiania atrybutów danych.

Ja również napotkałem ten problem i stwierdziłem, że wydaje się, że jest to po prostu formatowanie nazwy atrybutu danych .

Z mojego doświadczenia wynika, że ​​należy unikać używania łączników w zmiennej danych (nazwa zmiennej występująca po „ dane- ”).

To nie zadziałało dla mnie:

[Narzut]

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

[jQuery]

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

Ale poniższe działały dobrze! :):

(W razie potrzeby używam podkreślenia zamiast łącznika)

[Narzut]

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

[jQuery]

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

Mam nadzieję, że to pomoże. Pozdrawiam wszystkich!

mmmdearte
źródło