jQuery .data () tidak berfungsi, tetapi .attr () berfungsi

107

Maafkan saya karena tidak lebih spesifik tentang ini. Saya memiliki bug yang aneh. Setelah dokumen dimuat, saya mengulang beberapa elemen yang awalnya memiliki data-itemname="", dan saya menetapkan nilai tersebut menggunakan .attr("data-itemname", "someValue").

Masalah: Ketika saya kemudian mengulang melalui elemen-elemen itu, jika saya melakukannya elem.data().itemname, saya mengerti "", tetapi jika saya melakukannya elem.attr("data-itemname"), saya mengerti "someValue". Ini seperti .data()getter jQuery hanya mendapatkan elemen yang pada awalnya ditetapkan untuk berisi beberapa nilai, tetapi jika awalnya kosong, dan kemudian disetel, maka .data()tidak akan mendapatkan nilainya nanti.

Saya telah mencoba membuat ulang bug ini tetapi belum bisa.

Edit

Saya telah membuat ulang bug! http://jsbin.com/ihuhep/edit#javascript,html,live

Juga, cuplikan dari tautan di atas ...

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
sumber
4
Berusahalah lebih keras untuk membuat ulang bug :-)
dkamins
1
Bukan elem.data("itemname")tidak elem.data().itemname?
Hogan
sementara elem.data (). itemname akan membiarkan Anda membaca nilai itu TIDAK AKAN membiarkan Anda mengatur nilainya. (Dengan demikian elem.data().itemname = somevalue;tidak mengubah data yang mendasarinya.)
Hogan
@dkamins - Saya telah membuat ulang bug tersebut, silakan lihat versi yang telah diedit.
Ian Davis

Jawaban:

210

Saya mengalami "bug" serupa beberapa hari yang lalu saat bekerja dengan .data()dan .attr('data-name')untuk atribut data HTML5.

Perilaku yang Anda gambarkan bukanlah bug, tetapi memang disengaja.

The .data()panggilan khusus - bukan hanya tidak mengambil HTML5 Data atribut itu juga upaya untuk mengevaluasi / mengurai atribut. Jadi dengan atribut seperti data-myjson='{"hello":"world"}'saat diambil via .data()akan mengembalikan Objectsementara pengambilan via .attr()akan mengembalikan sebuah string. Lihat contoh jsfiddle.

Karena .data()pemrosesan tambahan, jQuery menyimpan hasil evaluasi atribut dalam $.cache- setelah semua, setelah atribut data dievaluasi, akan sia-sia untuk mengevaluasi ulang pada setiap .data()panggilan - terutama karena variabel data dapat berisi string JSON yang kompleks.

Saya mengatakan semua itu untuk mengatakan yang berikut: Setelah mengambil atribut melalui .data()perubahan apa pun yang dibuat oleh .attr('data-myvar', '')tidak akan terlihat oleh .data()panggilan berikutnya . Uji ini di jsfiddle.

Untuk menghindari masalah ini jangan mencampurkan .datadan .attr()menelepon. Gunakan satu atau lainnya.

leepowers
sumber
menandai ini sebagai jawaban. silakan lihat pengujian ini untuk semua kemungkinan skenario dengan .data () vs. .attr (), yang mencakup mendapatkan & menyetel menggunakan masing-masing, dan mengeluarkan panjang selektor setelah disetel, jsbin.com/acegef/edit# javascript, html, langsung
Ian Davis
9
Setidaknya ini menjelaskan perilaku yang tampaknya non-intuitif ... meskipun ini dapat menyebabkan beberapa sakit kepala yang lebih kecil ketika seseorang menggunakan perpustakaan pihak ke-3, beberapa di antaranya hanya menggunakan data (), dan lainnya mengubah atribut sebenarnya.
Haroldo_OK
1
@leepowers, "Setelah mengambil atribut melalui .data () perubahan apa pun yang dilakukan oleh .attr ('data-myvar', '') tidak akan terlihat oleh panggilan .data () berikutnya", jika itu benar-benar karena cache ( $ .cache), maka sepertinya tidak bagus! alih-alih mengandalkan secara membabi buta pada cache, panggilan .data () berikutnya dapat melakukan beberapa pemeriksaan dasar () seperti apakah nilainya kosong sekarang atau panjang string berubah (mencocokkan cache dengan nilai saat ini)
minhajul
1
Perlu dicatat bahwa mencoba menyetel data ('id', val) juga gagal jika nilainya tidak diambil sebelumnya ... desain hebat dari fungsi data () oleh Jquery.
andreszs
3
Jadi, cache data () itulah yang menahan saya selama berjam-jam. Tidak heran mengapa tidak peduli seberapa banyak saya mengubah nilainya, ia tetap mengembalikan nilai yang sama. Terima kasih untuk ini.
Lynnell Emmanuel Neri
17

Ini adalah hasil dari kesalahpahaman: dataBUKAN aksesor untuk data-*atribut . Lebih dan kurang dari itu.

dataadalah aksesor untuk data cache jQuery pada elemen. Cache tersebut diinisialisasi dari data-*atribut jika ada, tetapi datatidak pernah menulis ke atribut, juga tidak mengubah atribut mengubah cache data setelah inisialisasi:

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>

datajuga memijat apa yang ditemukannya dengan berbagai cara, menebak tipe data, membuat data("answer")elemen dengan data-answer="42"angka, bukan string, atau bahkan mem-parsing hal-hal sebagai JSON jika terlihat seperti 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>

Jika Anda ingin menggunakan atribut (baik membaca dan menyetelnya), gunakan attr, bukan data. attr adalah aksesor untuk atribut.

TJ Crowder
sumber
6

Itu karena nama atributnya adalah data-itemname. Anda tidak dapat menggunakan notasi -singkatan obj.attribute(obj.data-nama item akan diartikan sebagai "obj.data minus itemname").

Marc B
sumber
kata siapa? Dapatkah Anda menyediakan sebuah sambungan?
jahrichie
6

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

.data("itemname", "someValue") memodifikasi cache jQuery.

Untuk mendapatkan ini berfungsi dalam mengikuti Javascript dan sebagai tambahan di CSS Anda harus mengatur keduanya.

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

Mengapa Anda tidak menggunakannya di .data()mana-mana?

Anda juga dapat mendeklarasikan nilai default sebaris pada HTML, yang juga tidak masalah.

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

dan

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

jika Anda ingin mengubahnya, lakukan saja

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

dan untuk menghapusnya, Anda dapat meminta

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

Anda harus benar-benar mencoba dan menghindari penggunaan .attr("data-*"), saya tidak tahu mengapa Anda ingin melakukannya.

bevacqua.dll
sumber
s / should / must, menggunakan .attr('data-*', ...)tidak akan membuat data terlihat oleh.data()
ThiefMaster
Tetapi bukankah melakukannya dengan attr () seperti yang harus Anda lakukan tanpa jQuery? (dengan getAttribute tho)
powerbuoy
Jika Anda tidak memiliki jQuery yang Anda gunakan getAttribute()dan setAttribute()- jadi kedua metode akan mengakses atribut sebenarnya dan akan berfungsi kembali. Atau Anda cukup menggunakan dataSetproperti yang disediakan browser modern.
ThiefMaster
1
Jangan pilih elemen seperti itu. Ini sangat tidak efisien karena harus mengulang setiap elemen dalam dokumen. Gunakan a classsebagai gantinya karena browser memiliki fungsi asli untuk mendapatkan elemen dengan kelas tertentu.
ThiefMaster
1
tetapi, "555" adalah data, jadi saya harus menggunakan logika data (). memasukkan data tersebut ke dalam nama kelas adalah mencampurkan data dengan presentasi. cara yang berbeda untuk melakukannya, saya kira.
Ian Davis
1

Anda harus mengatur data menggunakan .data('itemname', 'someValue');. Menggunakan .attr()untuk menyetel atribut data tidak akan berfungsi: http://jsfiddle.net/ThiefMaster/YHsKx/

Namun, Anda dapat memberikan nilai sebaris dengan menggunakan misalnya <div data-key="value">di markup.

ThiefMaster
sumber
di jsfiddle, get bekerja setelah Anda melakukan kedua set. jadi, melakukan attr ("data-itemname", "value") TIDAK berhasil. Saya menggunakan Chrome.
Ian Davis
@Ian uh, tidak. yang .data()panggilan menetapkan atribut, sedangkan .attr()panggilan tidak apa-apa.
bevacqua
@IanDavis: Klik "set attr" dan "get" akan memberi Anda objek kosong. Hanya "set data" yang berfungsi.
ThiefMaster
@Nico - inilah yang saya lakukan: (1) klik tombol "set attr", lalu (2) klik tombol "get". inilah yang terjadi: itu mengingatkan saya, "{" test ":" meong "}". jadi, .attr () menyetel atributnya. jika tidak, peringatan akan kosong. Jika Anda mengikuti langkah-langkah tersebut dengan tepat, apakah Anda mendapatkan hasil yang berbeda? Terima kasih.
Ian Davis
1
@ ThiefMaster - di theaterA dalam kode yang saya berikan, saya tidak menggunakan .attr()sama sekali, hanya .data(), dan, panjang pemilih $(".someLink[data-tofilllater='theater5link']"),, adalah nol. jadi sepertinya saya harus menggunakan .attr(): /
Ian Davis
0

Saya dapat melihat bahwa ini telah memunculkan beberapa divisi tentang bagaimana mendekati pengaturan atribut data.

Saya juga mengalami masalah ini dan saya menemukan bahwa masalahnya tampaknya hanya format nama atribut data .

Dalam pengalaman saya, Anda harus menghindari penggunaan tanda hubung dalam variabel data (nama variabel yang muncul setelah " data- ").

Ini tidak berhasil untuk saya:

[Markup]

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

[jQuery]

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

Tapi berikut ini bekerja dengan baik! :):

(Saya menggunakan garis bawah dan bukan tanda hubung bila diperlukan)

[Markup]

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

[jQuery]

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

Saya harap ini membantu. Cheers semuanya!

mmmdearte
sumber