pemilih data jquery

184

Saya perlu memilih elemen berdasarkan nilai yang disimpan dalam objek elemen .data(). Minimal, saya ingin memilih properti data tingkat atas menggunakan penyeleksi, mungkin seperti ini:

$('a').data("category","music");
$('a:data(category=music)');

Atau mungkin pemilih akan berada dalam format pemilih atribut biasa:

$('a[category=music]');

Atau dalam format atribut, tetapi dengan specifier untuk menunjukkannya ada di .data():

$('a[:category=music]');

Saya menemukan implementasi James Padolsey terlihat sederhana, namun bagus. Format pemilih di atas metode mirror ditunjukkan pada halaman itu. Ada juga patch Sizzle ini .

Untuk beberapa alasan, saya ingat pernah membaca beberapa waktu lalu bahwa jQuery 1.4 akan menyertakan dukungan untuk penyeleksi pada nilai-nilai dalam .data()objek jquery . Namun, sekarang saya sedang mencarinya, saya tidak dapat menemukannya. Mungkin itu hanya permintaan fitur yang saya lihat. Apakah ada dukungan untuk ini dan saya tidak melihatnya?

Idealnya, saya ingin mendukung sub-properti dalam data () menggunakan notasi titik. Seperti ini:

$('a').data("user",{name: {first:"Tom",last:"Smith"},username: "tomsmith"});
$('a[:user.name.first=Tom]');

Saya juga ingin mendukung beberapa penyeleksi data, di mana hanya elemen dengan SEMUA pemilih data yang ditentukan yang ditemukan. Pemilih berganda jquery biasa melakukan operasi ATAU. Misalnya, $('a.big, a.small')pilih atag dengan kelas bigatau small). Saya mencari DAN, mungkin seperti ini:

$('a').data("artist",{id: 3281, name: "Madonna"});
$('a').data("category","music");
$('a[:category=music && :artist.name=Madonna]');

Terakhir, akan lebih bagus jika operator perbandingan dan fitur regex tersedia pada pemilih data. Jadi $(a[:artist.id>5000])mungkin saja. Saya menyadari saya mungkin bisa melakukan banyak hal menggunakan ini filter(), tetapi akan menyenangkan untuk memiliki format pemilih yang sederhana.

Solusi apa yang tersedia untuk melakukan ini? Apakah Jame's Padolsey adalah solusi terbaik saat ini? Perhatian saya terutama dalam hal kinerja, tetapi juga dalam fitur tambahan seperti notasi titik sub-properti dan beberapa pemilih data. Apakah ada implementasi lain yang mendukung hal-hal ini atau lebih baik dalam beberapa hal?

Tauren
sumber
Gunakan jquery ui core api.jqueryui.com/category/ui-core Ini memiliki pemilih: data ()
regisbsb

Jawaban:

101

Saya telah membuat datapemilih baru yang memungkinkan Anda untuk melakukan query dan kondisi bersarang. Pemakaian:

$('a:data(category==music,artist.name==Madonna)');

Polanya adalah:

:data( {namespace} [{operator} {check}]  )

"operator" dan "centang" adalah opsional. Jadi, jika Anda hanya memiliki :data(a.b.c)itu hanya akan memeriksa truthiness dari a.b.c.

Anda dapat melihat operator yang tersedia dalam kode di bawah ini. Di antara mereka adalah ~=yang memungkinkan pengujian regex:

$('a:data(category~=^mus..$,artist.name~=^M.+a$)');

Saya sudah mengujinya dengan beberapa variasi dan sepertinya berfungsi cukup baik. Saya mungkin akan menambahkan ini sebagai repo Github segera (dengan test suite lengkap), jadi tetap perhatikan!

Kode:

(function(){

    var matcher = /\s*(?:((?:(?:\\\.|[^.,])+\.?)+)\s*([!~><=]=|[><])\s*("|')?((?:\\\3|.)*?)\3|(.+?))\s*(?:,|$)/g;

    function resolve(element, data) {

        data = data.match(/(?:\\\.|[^.])+(?=\.|$)/g);

        var cur = jQuery.data(element)[data.shift()];

        while (cur && data[0]) {
            cur = cur[data.shift()];
        }

        return cur || undefined;

    }

    jQuery.expr[':'].data = function(el, i, match) {

        matcher.lastIndex = 0;

        var expr = match[3],
            m,
            check, val,
            allMatch = null,
            foundMatch = false;

        while (m = matcher.exec(expr)) {

            check = m[4];
            val = resolve(el, m[1] || m[5]);

            switch (m[2]) {
                case '==': foundMatch = val == check; break;
                case '!=': foundMatch = val != check; break;
                case '<=': foundMatch = val <= check; break;
                case '>=': foundMatch = val >= check; break;
                case '~=': foundMatch = RegExp(check).test(val); break;
                case '>': foundMatch = val > check; break;
                case '<': foundMatch = val < check; break;
                default: if (m[5]) foundMatch = !!val;
            }

            allMatch = allMatch === null ? foundMatch : allMatch && foundMatch;

        }

        return allMatch;

    };

}());
James
sumber
@ JP: Sangat manis, aku tahu aku bisa mengandalkanmu! Pergi tidur sekarang, tapi aku akan mencobanya besok. Apakah mungkin untuk melakukan operasi ATAU juga? Saya tidak membutuhkannya, hanya ingin tahu.
Tauren
1
@ JP: Juga, saya ingin tahu tentang pendapat Anda tentang solusi pemilih data lainnya, pro / kontra untuk Anda vs mereka. Misalnya, plugin ini: plugins.jquery.com/project/dataSelector .
Tauren
1
Dimungkinkan untuk melakukan operasi ATAU, tetapi mungkin yang terbaik adalah hanya memiliki dua penyeleksi, $("a:data(condition),a:data(orCondition)")... itu akan memiliki efek yang sama. Semakin banyak fitur yang Anda tambahkan, semakin lambat. Jika logikanya kompleks, maka gunakan $(foo).filter(function(){...}).
James
3
@ JP: Apakah Anda pernah mengubah ini menjadi proyek github? Saya menjalankan tes menggunakan jQuery 1.5.1 menggunakan pemilih $ ("input: data (test> 400)") pada input dengan atribut html5 data-test = "500" tetapi pemilih tidak mengembalikan apa pun.
James South
1
@ JamesSouth Itu karena pemilih dalam jawaban ini menggunakan level rendah jQuery.data, yang tidak mendapatkan data yang ditentukan dalam atribut HTML5. Jika Anda ingin fungsi itu, Anda dapat mengubah jQuery.datake $('selector').data, tapi itu trade off untuk kecepatan.
Shef
175

Saat ini saya memilih seperti ini:

$('a[data-attribute=true]')

Yang sepertinya berfungsi dengan baik, tetapi alangkah baiknya jika jQuery dapat memilih oleh atribut itu tanpa awalan 'data-'.

Saya belum menguji ini dengan data yang ditambahkan ke elemen melalui jQuery secara dinamis, sehingga bisa menjadi kejatuhan metode ini.

Abu
sumber
Ini persis apa yang saya cari. Manis
MikeMurko
109
Ini tidak benar-benar berfungsi jika Anda melampirkan / memodifikasi data melalui JS melalui fungsi .data (), pemilih atribut hanya memeriksa DOM, JS menyimpan .data () dalam memori
Clarence Liu
1
Bisakah Anda memberi tahu bagaimana Anda mengakses berdasarkan atribut jika Anda melampirkan melalui. Data ()?
Maxim V. Pavlov
1
Saya menyarankan bahwa jawaban lain yang disediakan di utas ini adalah pilihan yang lebih baik untuk apa pun selain kasus penggunaan yang sangat sederhana.
Ash
Solusi Dmitri terlihat bagus bagi saya karena tidak bergantung pada kode pihak ketiga.
Ash
83

Anda juga dapat menggunakan fungsi penyaringan sederhana tanpa plugin apa pun. Ini bukan apa yang Anda inginkan tetapi hasilnya sama:

$('a').data("user", {name: {first:"Tom",last:"Smith"},username: "tomsmith"});

$('a').filter(function() {
    return $(this).data('user') && $(this).data('user').name.first === "Tom";
});
Dmitri
sumber
2
Ini adalah ide yang bagus, saya lupa filterfungsi traversal dapat menerima fungsi tes =) terima kasih
Clarence Liu
Bagaimana saya bisa melakukan "berisi" alih-alih sama dengan Tom?
Alisso
1
Alisso, alih-alih name.first == "Tom" gunakan name.first && name.first.indexOF ("Tom")> -1;
Dmitri
24

Saya ingin memperingatkan Anda bahwa $('a[data-attribute=true]')itu tidak berfungsi, sesuai jawaban Ashley, jika Anda melampirkan data ke elemen DOM melalui fungsi data ().

Ini berfungsi seperti yang Anda harapkan jika Anda menambahkan data-attr aktual dalam HTML Anda, tetapi jQuery menyimpan data dalam memori, sehingga hasil yang Anda dapatkan dari $('a[data-attribute=true]')tidak benar.

Anda harus menggunakan plugin data http://code.google.com/p/jquerypluginsblog/ , menggunakan filtersolusi Dmitri , atau melakukan $ .each semua elemen dan periksa. Data () secara iteratif

Clarence Liu
sumber
Dalam kasus saya, itu baik-baik saja karena saya pra-mengisi bidang di sisi server dan hanya perlu berkonsultasi dengan cara ini pada beban awal. Filter mungkin sama cepat (masing-masing hampir pasti lebih lambat) tetapi ini menang pada keterbacaan.
Don
11

Ada :data()plugin filter yang melakukan ini :)

Beberapa contoh berdasarkan pertanyaan Anda:

$('a:data("category=music")')
$('a:data("user.name.first=Tom")');
$('a:data("category=music"):data("artist.name=Madonna")');
//jQuery supports multiple of any selector to restrict further, 
//just chain with no space in-between for this effect

Kinerja tidak akan menjadi sangat hebat dibandingkan dengan apa yang mungkin , memilih $._cachedan mengambil elemen yang sesuai sejauh ini adalah yang tercepat, tetapi jauh lebih banyak tentang dan tidak terlalu "jQuery-ey" dalam hal bagaimana Anda mendapatkan barang (Anda biasanya datang dari sisi elemen). Dari atas kepala saya, saya tidak yakin ini tercepat karena proses pergi dari ID unik ke elemen berbelit-belit dalam hal kinerja.

Selektor perbandingan yang Anda sebutkan akan paling baik dilakukan di a .filter(), tidak ada dukungan bawaan untuk ini di plugin, meskipun Anda bisa menambahkannya tanpa banyak masalah.

Nick Craver
sumber
terima kasih atas jawaban yang menyeluruh. Apakah Anda tahu jika menggunakan data-*atribut HTML5 dan memilihnya akan lebih cepat daripada memilih pada .data()properti? Juga, ada ide di mana saya bisa mengetahui lebih lanjut tentang $ ._ cache? Saya mencari di Google untuk itu, tetapi tidak menemukan banyak.
Tauren
@Tauren - Kesalahan saya $.cachebukan $._cache, Anda bisa melihat bagaimana itu diterapkan dan digunakan dalam jQuery core di sini: github.com/jquery/jquery/blob/master/src/data.js#L4 Ketika Anda menyebut .data()itu sebenarnya menyimpannya sebagai objek di $.cache[elementUniqueID], yang merupakan ID yang diberikan sesuai kebutuhan dengan cara tambahan untuk setiap elemen, misalnya, 1, 2, 3, dll. ID pendakian itu akan diekspos di jQuery 1.4.3 Saya percaya, berdasarkan komentar git beberapa hari yang lalu. Saya menganggap rute HTML 5 akan lebih cepat, tergantung optimasi browser apa yang tersedia (saya yakin akan tersedia lebih banyak).
Nick Craver
7

Anda dapat mengatur data-*atribut pada elm menggunakan attr(), dan kemudian pilih menggunakan atribut itu:

var elm = $('a').attr('data-test',123); //assign 123 using attr()
elm = $("a[data-test=123]"); //select elm using attribute

dan sekarang untuk elm itu, keduanya attr()dan data()akan menghasilkan 123 :

console.log(elm.attr('data-test')); //123
console.log(elm.data('test')); //123

Namun, jika Anda memodifikasi nilai menjadi 456 menggunakan attr(), data() masih akan menjadi 123 :

elm.attr('data-test',456); //modify to 456
elm = $("a[data-test=456]"); //reselect elm using new 456 attribute

console.log(elm.attr('data-test')); //456
console.log(elm.data('test')); //123

Jadi seperti yang saya mengerti, sepertinya Anda mungkin harus menghindari percampuran attr()dan data()perintah dalam kode Anda jika Anda tidak perlu. Karena attr()sepertinya berhubungan langsung dengan DOM sedangkan data()berinteraksi dengan 'memori', meskipun nilai awalnya bisa dari DOM. Tetapi poin kuncinya adalah bahwa keduanya belum tentu sinkron sama sekali.

Jadi berhati-hatilah.

Bagaimanapun, jika Anda tidak mengubah data-*atribut di DOM atau di memori, maka Anda tidak akan memiliki masalah. Segera setelah Anda mulai mengubah nilai adalah saat potensi masalah dapat muncul.

Terima kasih kepada @Clarence Liu atas jawaban @ Ash, dan juga pos ini .

D.Tate
sumber
5

Jika Anda juga menggunakan jQueryUI, Anda mendapatkan versi :datapemilih (sederhana) yang memeriksa keberadaan item data, sehingga Anda dapat melakukan sesuatu seperti $("div:data(view)"), atau $( this ).closest(":data(view)").

Lihat http://api.jqueryui.com/data-selector/ . Saya tidak tahu sudah berapa lama mereka memilikinya, tetapi sekarang sudah ada di sana!

Paul
sumber
Ini hanya memeriksa keberadaan item data seperti yang Anda katakan. Itu tidak memeriksa nilai-nilai (sesuai dengan dokumen). Senang mengetahui tentang ini
Fractalf
3

Ini adalah plugin yang menyederhanakan https://github.com/rootical/jQueryDataSelector

Gunakan seperti itu:

data selector           jQuery selector
  $$('name')              $('[data-name]')
  $$('name', 10)          $('[data-name=10]')
  $$('name', false)       $('[data-name=false]')
  $$('name', null)        $('[data-name]')
  $$('name', {})          Syntax error
Akar V.
sumber