Haruskah semua acara jquery terikat pada $ (dokumen)?

164

Dari mana ini berasal

Ketika saya pertama kali belajar jQuery, saya biasanya melampirkan acara seperti ini:

$('.my-widget a').click(function() {
    $(this).toggleClass('active');
});

Setelah mempelajari lebih lanjut tentang kecepatan pemilih dan delegasi acara, saya membaca di beberapa tempat bahwa "delegasi acara jQuery akan membuat kode Anda lebih cepat." Jadi saya mulai menulis kode seperti ini:

$('.my-widget').on('click','a',function() {
    $(this).toggleClass('active');
});

Ini juga merupakan cara yang disarankan untuk mereplikasi perilaku peristiwa .live () yang tidak digunakan lagi. Yang penting bagi saya karena banyak situs saya secara dinamis menambah / menghapus widget setiap saat. Namun di atas tidak berperilaku persis seperti .live (), karena hanya elemen yang ditambahkan ke wadah yang sudah ada '.my-widget' yang akan mendapatkan perilaku. Jika saya menambahkan secara dinamis blok html lain setelah kode tersebut berjalan, elemen-elemen itu tidak akan membuat acara terikat dengannya. Seperti ini:

setTimeout(function() {
    $('body').append('<div class="my-widget"><a>Click does nothing</a></div>');
}, 1000);


Apa yang ingin saya capai:

  1. perilaku lama .live () // yang berarti melampirkan peristiwa ke elemen yang belum ada
  2. manfaat .on ()
  3. kinerja tercepat untuk mengikat acara
  4. Cara sederhana untuk mengelola acara

Saya sekarang melampirkan semua acara seperti ini:

$(document).on('click.my-widget-namespace', '.my-widget a', function() {
    $(this).toggleClass('active');
});

Yang sepertinya memenuhi semua tujuan saya. (Ya itu lebih lambat di IE untuk beberapa alasan, tidak tahu mengapa?) Ini cepat karena hanya satu peristiwa diikat ke elemen tunggal dan pemilih sekunder hanya dievaluasi ketika peristiwa terjadi (perbaiki saya jika ini salah di sini). Namespace mengagumkan karena membuatnya lebih mudah untuk beralih pendengar acara.

Solusi / Pertanyaan Saya

Jadi saya mulai berpikir bahwa acara jQuery harus selalu terikat ke $ (dokumen).
Apakah ada alasan mengapa Anda tidak ingin melakukan ini?
Mungkinkah ini dianggap praktik terbaik? Jika tidak, mengapa?

Jika Anda sudah membaca semua ini, terima kasih. Saya menghargai setiap / semua umpan balik / wawasan.

Asumsi:

  1. Menggunakan jQuery yang mendukung .on()// setidaknya versi 1.7
  2. Anda ingin acara tersebut ditambahkan ke konten yang ditambahkan secara dinamis

Bacaan / Contoh:

  1. http://24ways.org/2011/your-jquery-now-with-less-suck
  2. http://brandonaaron.net/blog/2010/03/4/event-delegation-with-jquery
  3. http://www.jasonbuckboyer.com/playground/speed/speed.html
  4. http://api.jquery.com/on/
Buck
sumber

Jawaban:

215

Tidak - Anda TIDAK boleh mengikat semua penangan acara yang didelegasikan ke documentobjek. Itu mungkin skenario berkinerja terburuk yang bisa Anda buat.

Pertama, delegasi acara tidak selalu membuat kode Anda lebih cepat. Dalam beberapa kasus, ini menguntungkan dan dalam beberapa kasus tidak. Anda harus menggunakan delegasi acara ketika Anda benar-benar membutuhkan delegasi acara dan ketika Anda mendapat manfaat darinya. Jika tidak, Anda harus mengikat penangan acara langsung ke objek tempat acara terjadi karena ini umumnya akan lebih efisien.

Kedua, Anda TIDAK boleh mengikat semua acara yang didelegasikan di tingkat dokumen. Inilah sebabnya mengapa .live()sudah usang karena ini sangat tidak efisien ketika Anda memiliki banyak peristiwa terikat dengan cara ini. Untuk penanganan acara yang didelegasikan, JAUH lebih efisien untuk mengikat mereka ke induk terdekat yang tidak dinamis.

Ketiga, tidak semua acara bekerja atau semua masalah dapat diselesaikan dengan delegasi. Misalnya, jika Anda ingin mencegat peristiwa utama pada kontrol input dan memblokir kunci yang tidak valid agar tidak dimasukkan ke dalam kontrol input, Anda tidak dapat melakukan itu dengan penanganan acara yang didelegasikan karena pada saat acara muncul hingga penangan yang didelegasikan, itu sudah telah diproses oleh kontrol input dan sudah terlambat untuk mempengaruhi perilaku itu.

Inilah saat-saat ketika delegasi acara diperlukan atau menguntungkan:

  • Ketika objek yang Anda tangkap peristiwa secara dinamis dibuat / dihapus dan Anda masih ingin menangkap peristiwa pada mereka tanpa harus secara eksplisit me-rebind event handler setiap kali Anda membuat yang baru.
  • Ketika Anda memiliki banyak objek yang semuanya ingin penangan event yang sama persis (di mana banyak setidaknya ratusan). Dalam hal ini, mungkin lebih efisien pada waktu pemasangan untuk mengikat satu pengendali acara yang didelegasikan daripada ratusan atau lebih penangan acara langsung. Catatan, penanganan acara yang didelegasikan selalu kurang efisien pada waktu berjalan dibandingkan penangan acara langsung.
  • Saat Anda mencoba menangkap (pada level yang lebih tinggi dalam dokumen Anda) peristiwa yang terjadi pada elemen apa pun dalam dokumen.
  • Saat desain Anda secara eksplisit menggunakan event bubbling dan stopPropagation () untuk menyelesaikan beberapa masalah atau fitur di halaman Anda.

Untuk memahami ini sedikit lagi, orang perlu memahami bagaimana penangan event yang didelegasikan jQuery bekerja. Ketika Anda memanggil sesuatu seperti ini:

$("#myParent").on('click', 'button.actionButton', myFn);

Itu menginstal pengendali event jQuery generik pada #myParentobjek. Ketika sebuah event klik muncul hingga pengendali event yang didelegasikan ini, jQuery harus melihat daftar handler event yang didelegasikan yang dilampirkan pada objek ini dan melihat apakah elemen yang berasal dari event tersebut cocok dengan salah satu pemilih dalam handler event yang didelegasikan.

Karena penyeleksi dapat terlibat secara adil, ini berarti bahwa jQuery harus mengurai setiap pemilih dan kemudian membandingkannya dengan karakteristik target acara asli untuk melihat apakah itu cocok dengan setiap pemilih. Ini bukan operasi yang murah. Bukan masalah besar jika hanya ada satu di antara mereka, tetapi jika Anda menempatkan semua penyeleksi Anda pada objek dokumen dan ada ratusan penyeleksi untuk dibandingkan dengan setiap peristiwa yang menggelegak, ini secara serius dapat mulai menghambat kinerja penanganan acara.

Untuk alasan ini, Anda ingin mengatur penangan acara yang didelegasikan Anda sehingga penangan acara yang didelegasikan sedekat mungkin dengan objek target. Ini berarti bahwa lebih sedikit acara akan muncul melalui masing-masing pengendali acara yang didelegasikan, sehingga meningkatkan kinerja. Menempatkan semua acara yang didelegasikan pada objek dokumen adalah kinerja yang paling buruk karena semua acara yang digelembungkan harus melalui semua penangan acara yang didelegasikan dan dievaluasi terhadap semua penyeleksi acara yang mungkin didelegasikan. Inilah sebabnya mengapa .live()usang karena ini adalah apa yang .live()dilakukan dan terbukti sangat tidak efisien.


Jadi, untuk mencapai kinerja yang dioptimalkan:

  1. Hanya gunakan penanganan acara yang didelegasikan ketika sebenarnya menyediakan fitur yang Anda butuhkan atau meningkatkan kinerja. Jangan hanya selalu menggunakannya karena mudah karena ketika Anda tidak benar-benar membutuhkannya. Ini sebenarnya berkinerja lebih buruk pada waktu pengiriman acara daripada mengikat acara langsung.
  2. Lampirkan penangan acara yang didelegasikan kepada orang tua terdekat ke sumber acara sebanyak mungkin. Jika Anda menggunakan penanganan acara yang didelegasikan karena Anda memiliki elemen dinamis yang ingin Anda tangkap acara, maka pilih induk terdekat yang tidak dinamis itu sendiri.
  3. Gunakan penyeleksi yang mudah dievaluasi untuk penangan acara yang didelegasikan. Jika Anda mengikuti cara kerja penanganan acara yang didelegasikan, Anda akan memahami bahwa event handler yang didelegasikan harus dibandingkan dengan banyak objek berkali-kali sehingga memilih pemilih seefisien mungkin atau menambahkan kelas sederhana ke objek Anda sehingga penyeleksi yang lebih sederhana dapat digunakan akan meningkatkan kinerja penanganan acara yang didelegasikan.
pacar00
sumber
2
Luar biasa. Terima kasih atas uraiannya yang cepat dan terperinci. Itu masuk akal bahwa waktu pengiriman acara akan meningkat dengan menggunakan .on () tapi saya pikir saya masih berjuang memutuskan antara peningkatan waktu pengiriman acara vs tim pemrosesan halaman awal. Saya umumnya mengurangi waktu halaman awal. Misalnya, jika saya memiliki 200 elemen pada satu halaman (dan lebih banyak ketika peristiwa terjadi), itu sekitar 100 kali lebih mahal pada beban awal (karena harus menambahkan 100 pendengar acara) daripada jika saya menambahkan pendengar acara tunggal ke dalam tautan
Buck
Juga ingin mengatakan bahwa Anda meyakinkan saya - Jangan ikat semua acara menjadi $ (dokumen). Bind ke orang tua terdekat yang tidak mengubah mungkin. Saya kira saya masih harus memutuskan berdasarkan kasus per kasus ketika delegasi acara lebih baik daripada langsung mengikat acara ke elemen. Dan ketika saya perlu acara terkait dengan konten dinamis (yang tidak memiliki orangtua yang tidak berubah) saya kira saya akan terus melakukan apa yang @Vega rekomendasikan di bawah ini dan cukup "mengikat pawang setelah isinya dimasukkan ke dalam (the ) DOM ", menggunakan parameter konteks jQuery di pemilih saya.
Buck
5
@ user1394692 - jika Anda memiliki banyak elemen yang hampir identik, maka mungkin masuk akal untuk menggunakan pengendali acara yang didelegasikan untuk mereka. Saya mengatakan itu dalam jawaban saya. Jika saya memiliki meja 500 baris raksasa dan memiliki tombol yang sama di setiap baris kolom tertentu, saya akan menggunakan satu pengendali acara yang didelegasikan di atas meja untuk melayani semua tombol. Tetapi jika saya memiliki 200 elemen dan mereka semua membutuhkan penangan acara unik mereka sendiri, memasang 200 penangan acara yang didelegasikan tidak lebih cepat daripada menginstal 200 penangan acara yang terikat langsung, namun penanganan acara yang didelegasikan mungkin jauh lebih lambat pada saat pelaksanaan acara.
jfriend00
Kamu sempurna. Setuju! Apakah saya mengerti bahwa ini adalah hal terburuk yang dapat saya lakukan - $(document).on('click', '[myCustomAttr]', function () { ... });?
Artem Fitiskin
Menggunakan pjax / turbolinks menimbulkan masalah menarik karena semua elemen dibuat / dihapus secara dinamis (terlepas dari pemuatan halaman pertama). Jadi apakah lebih baik untuk mengikat semua peristiwa sekali ke document, atau mengikat ke pilihan yang lebih spesifik page:loaddan membatalkan ikatan peristiwa ini page:after-remove? Hmmm
Dom Christie
9

Delegasi acara adalah teknik untuk menulis penangan Anda sebelum elemen tersebut benar-benar ada di DOM. Metode ini memiliki kekurangannya sendiri dan harus digunakan hanya jika Anda memiliki persyaratan seperti itu.

Kapan Anda harus menggunakan delegasi acara?

  1. Ketika Anda mengikat penangan umum untuk lebih banyak elemen yang membutuhkan fungsionalitas yang sama. (Mis: hover baris tabel)
    • Dalam contoh, jika Anda harus mengikat semua baris menggunakan ikatan langsung, Anda akhirnya akan membuat n handler untuk n baris di tabel itu. Dengan menggunakan metode pendelegasian, Anda bisa menangani semua itu dalam 1 penangan sederhana.
  2. Ketika Anda menambahkan konten dinamis lebih sering di DOM (Mis: Tambah / hapus baris dari tabel)

Mengapa Anda tidak menggunakan delegasi acara?

  1. Delegasi acara lebih lambat jika dibandingkan dengan mengikat acara langsung ke elemen.
    • Ini membandingkan pemilih target pada setiap gelembung yang terkena, perbandingannya akan semahal rumit.
  2. Tidak ada kontrol atas peristiwa yang menggelegak sampai menyentuh elemen yang terikat padanya.

PS: Bahkan untuk konten dinamis Anda tidak harus menggunakan metode delegasi acara jika Anda mengikat pawang setelah konten dimasukkan ke DOM. (Jika konten dinamis ditambahkan tidak sering dihapus / ditambahkan kembali)

Selvakumar Arumugam
sumber
0

Saya ingin menambahkan beberapa komentar dan argumen balasan untuk jawaban jfriend00. (Sebagian besar hanya pendapat saya berdasarkan perasaan saya)

Tidak - Anda TIDAK boleh mengikat semua penangan acara yang didelegasikan ke objek dokumen. Itu mungkin skenario berkinerja terburuk yang bisa Anda buat.

Pertama, delegasi acara tidak selalu membuat kode Anda lebih cepat. Dalam beberapa kasus, ini menguntungkan dan dalam beberapa kasus tidak. Anda harus menggunakan delegasi acara ketika Anda benar-benar membutuhkan delegasi acara dan ketika Anda mendapat manfaat darinya. Jika tidak, Anda harus mengikat penangan acara langsung ke objek tempat acara terjadi karena ini umumnya akan lebih efisien.

Meskipun benar bahwa kinerja mungkin sedikit lebih baik jika Anda hanya akan mendaftar dan mengadakan satu elemen, saya percaya itu tidak membebani manfaat skalabilitas yang dibawa oleh delegasi. Saya juga percaya browser akan menangani ini lebih dan lebih efisien, meskipun saya tidak punya bukti tentang ini. Menurut pendapat saya, delegasi acara adalah jalan yang harus dilalui!

Kedua, Anda TIDAK boleh mengikat semua acara yang didelegasikan di tingkat dokumen. Inilah sebabnya mengapa .live () tidak digunakan lagi karena ini sangat tidak efisien ketika Anda memiliki banyak acara yang terikat dengan cara ini. Untuk penanganan acara yang didelegasikan, JAUH lebih efisien untuk mengikat mereka ke induk terdekat yang tidak dinamis.

Saya agak setuju dengan ini. Jika Anda 100% yakin bahwa suatu peristiwa hanya akan terjadi di dalam wadah, masuk akal untuk mengikat acara ke wadah ini, tapi saya masih akan berdebat menentang acara mengikat ke elemen pemicu secara langsung.

Ketiga, tidak semua acara bekerja atau semua masalah dapat diselesaikan dengan delegasi. Misalnya, jika Anda ingin mencegat peristiwa utama pada kontrol input dan memblokir kunci yang tidak valid agar tidak dimasukkan ke dalam kontrol input, Anda tidak dapat melakukan itu dengan penanganan acara yang didelegasikan karena pada saat acara muncul hingga penangan yang didelegasikan, itu sudah telah diproses oleh kontrol input dan sudah terlambat untuk mempengaruhi perilaku itu.

Ini tidak benar. Silakan lihat kode iniPen: https://codepen.io/pwkip/pen/jObGmjq

document.addEventListener('keypress', (e) => {
    e.preventDefault();
});

Ini menggambarkan bagaimana Anda dapat mencegah pengguna mengetik dengan mendaftarkan acara penekanan tombol pada dokumen.

Inilah saat-saat ketika delegasi acara diperlukan atau menguntungkan:

Ketika objek yang Anda tangkap peristiwa secara dinamis dibuat / dihapus dan Anda masih ingin menangkap peristiwa pada mereka tanpa harus secara eksplisit me-rebind event handler setiap kali Anda membuat yang baru. Ketika Anda memiliki banyak objek yang semuanya ingin penangan event yang sama persis (di mana banyak setidaknya ratusan). Dalam hal ini, mungkin lebih efisien pada waktu pemasangan untuk mengikat satu pengendali acara yang didelegasikan daripada ratusan atau lebih penangan acara langsung. Catatan, penanganan acara yang didelegasikan selalu kurang efisien pada waktu berjalan dibandingkan penangan acara langsung.

Saya ingin membalas dengan kutipan ini dari https://ehsangazar.com/optimizing-javascript-event-listeners-for-performance-e28406ad406c

Event delegation promotes binding as few DOM event handlers as possible, since each event handler requires memory. For example, let’s say that we have an HTML unordered list we want to bind event handlers to. Instead of binding a click event handler for each list item (which may be hundreds for all we know), we bind one click handler to the parent unordered list itself.

Juga, googling untuk performance cost of event delegation googlemengembalikan hasil lebih banyak dalam mendukung delegasi acara.

Saat Anda mencoba menangkap (pada level yang lebih tinggi dalam dokumen Anda) peristiwa yang terjadi pada elemen apa pun dalam dokumen. Saat desain Anda secara eksplisit menggunakan event bubbling dan stopPropagation () untuk menyelesaikan beberapa masalah atau fitur di halaman Anda. Untuk memahami ini sedikit lagi, orang perlu memahami bagaimana penangan event yang didelegasikan jQuery bekerja. Ketika Anda memanggil sesuatu seperti ini:

$ ("# myParent"). on ('klik', 'button.actionButton', myFn); Itu menginstal pengendali event jQuery generik pada objek #myParent. Ketika sebuah event klik muncul hingga pengendali event yang didelegasikan ini, jQuery harus melihat daftar handler event yang didelegasikan yang dilampirkan pada objek ini dan melihat apakah elemen yang berasal dari event tersebut cocok dengan salah satu pemilih dalam handler event yang didelegasikan.

Karena penyeleksi dapat terlibat secara adil, ini berarti bahwa jQuery harus mengurai setiap pemilih dan kemudian membandingkannya dengan karakteristik target acara asli untuk melihat apakah itu cocok dengan setiap pemilih. Ini bukan operasi yang murah. Bukan masalah besar jika hanya ada satu di antara mereka, tetapi jika Anda menempatkan semua penyeleksi Anda pada objek dokumen dan ada ratusan penyeleksi untuk dibandingkan dengan setiap peristiwa yang menggelegak, ini secara serius dapat mulai menghambat kinerja penanganan acara.

Untuk alasan ini, Anda ingin mengatur penangan acara yang didelegasikan Anda sehingga penangan acara yang didelegasikan sedekat mungkin dengan objek target. Ini berarti bahwa lebih sedikit acara akan muncul melalui masing-masing pengendali acara yang didelegasikan, sehingga meningkatkan kinerja. Menempatkan semua acara yang didelegasikan pada objek dokumen adalah kinerja yang paling buruk karena semua acara yang digelembungkan harus melalui semua penangan acara yang didelegasikan dan dievaluasi terhadap semua penyeleksi acara yang mungkin didelegasikan. Inilah sebabnya mengapa .live () tidak digunakan lagi karena inilah yang .live () lakukan dan terbukti sangat tidak efisien.

Di mana ini didokumentasikan? Jika itu benar, maka jQuery tampaknya menangani delegasi dengan cara yang sangat tidak efisien, dan kemudian argumen-argumen saya seharusnya hanya diterapkan pada vanilla JS.

Namun: Saya ingin mencari sumber resmi yang mendukung klaim ini.

:: EDIT ::

Sepertinya jQuery memang melakukan peristiwa yang menggelegak dengan cara yang sangat tidak efisien (karena mereka mendukung IE8) https://api.jquery.com/on/#event-performance

Jadi sebagian besar argumen saya di sini hanya berlaku untuk vanilla JS dan browser modern.

Jules Colle
sumber