Kamu menambahkan tombol hapus lewat JavaScript, lalu klik tombol itu tidak terjadi apa-apa. Penyebabnya bukan salah ketik selector. Listener dipasang saat tombol tersebut belum ada di DOM. Event delegation jQuery untuk elemen dinamis menyelesaikan ini dengan menempelkan satu listener pada induk yang sudah ada sejak awal.
Kenapa Listener Biasa Gagal pada Elemen Baru
Perintah $('.tombol-hapus').on('click', handler) bekerja dengan cara menelusuri DOM saat baris itu dijalankan, lalu memasang handler ke setiap elemen yang cocok. Kalau hasil pencariannya kosong, jQuery tidak memasang apa pun dan tidak melempar error. Tidak ada peringatan di console, jadi bug ini sering lolos saat pengujian manual.
Kasusnya muncul di pola yang sangat umum: tabel yang barisnya diisi dari API, daftar komentar yang ditambah lewat form, atau keranjang belanja yang isinya berubah. Elemen-elemen ini lahir setelah halaman selesai dimuat. Kalau kamu memakai cara menampilkan data API ke tabel HTML dengan jQuery sebagai rujukan, perhatikan bagian render barisnya, di situlah listener biasa mulai bermasalah.
Ada dua jalan keluar. Pertama, pasang ulang listener setiap kali selesai render. Cara ini bekerja, tapi rawan bocor karena listener lama tidak selalu dilepas, dan kode render jadi bercampur dengan kode interaksi. Kedua, pakai event delegation. Satu listener di induk, selesai.
Cara Kerja Event Delegation
Saat kamu mengklik sebuah tombol, browser tidak langsung mengirim event ke tombol itu saja. Event naik ke atas melalui setiap leluhur hingga ke document. Fase naik ini yang dimanfaatkan delegation.
Kalau listener dipasang di <table> dan bukan di tombol, maka klik pada tombol mana pun di dalamnya akan melewati table dan memicu listener tersebut. Di dalam handler, jQuery memeriksa elemen apa yang jadi target, apakah cocok dengan selector yang kamu berikan. Selama induknya masih ada di DOM, tombol yang baru dibuat pun otomatis ikut tertangani tanpa perlu didaftarkan ulang.
Yang dipasang di induk bukan salinan handler untuk tiap anak. Hanya ada satu handler yang berjalan, dan jQuery yang menentukan apakah event itu layak diproses berdasarkan selector. Karena itulah delegation lebih hemat memori pada daftar yang panjang.
Sintaks .on() dengan Selector Filter
Perbedaan delegation dan bukan terletak pada argumen kedua. Kalau argumen kedua berupa string selector, berarti delegation aktif.
// bukan delegation: hanya tombol yang ada saat ini
$('.tombol-hapus').on('click', function () {
$(this).closest('tr').remove();
});
// delegation: berlaku untuk tombol sekarang dan nanti
$('#tabel-user').on('click', '.tombol-hapus', function () {
$(this).closest('tr').remove();
});
Di dalam handler, this merujuk ke elemen yang cocok dengan selector, bukan ke induk tempat listener dipasang. Jadi $(this).closest('tr') tetap menemukan baris yang benar meski klik terjadi pada ikon di dalam tombol.
Kalau butuh melepasnya nanti, simpan referensi handler-nya. Pemanggilan .off('click', '.tombol-hapus') hanya efektif kalau selector-nya sama persis dengan saat dipasang.
Memilih Induk yang Tepat
Induk terbaik adalah elemen terdekat yang membungkus semua target dan tetap ada sepanjang umur halaman. Wadah tabel, panel, atau container daftar biasanya pilihan yang pas. Menempel ke document juga bekerja, tapi setiap klik di halaman akan melewatinya untuk diperiksa. Pada halaman sederhana tidak terasa, pada aplikasi yang padat interaksi bedanya mulai kelihatan.
Jebakan yang Sering Ditemui
- Selector terlalu spesifik. Menulis
table#tabel-user tbody tr td button.tombol-hapusmembuat delegasi rapuh. Begitu struktur HTML berubah sedikit, handler berhenti cocok. Cukup sebut kelas tombolnya. - Elemen non-focusable tidak menerima keyboard. Delegasi klik pada
<div>tidak otomatis bisa diakses lewat Tab. Pakai<button>asli supaya interaksi keyboard jalan tanpa tambahan apa pun. - Event yang tidak bubbling. Beberapa event seperti
focus,blur, danloadtidak naik ke atas. jQuery menutupi sebagian kasus ini denganfocusindanfocusout, tapi jangan berasumsi semua event bisa didelegasikan. - stopPropagation di handler anak. Kalau ada kode lain yang menghentikan bubbling sebelum mencapai induk, listener delegasi tidak pernah dipanggil.
- Dua handler saling menimpa. Memanggil
.on()berulang kali pada induk yang sama akan menumpuk handler dan membuat aksi berjalan berkali-kali. Pasang sekali saja.
Pola Praktis untuk Daftar yang Berubah
Bayangkan sebuah daftar tugas dengan tombol tambah, tandai selesai, dan hapus. Ketiganya dikerjakan lewat satu listener di wadah daftar.
$('#daftar-tugas').on('click', 'button', function () {
var $tombol = $(this);
var $item = $tombol.closest('li');
if ($tombol.hasClass('selesai')) {
$item.toggleClass('selesai');
} else if ($tombol.hasClass('hapus')) {
$item.remove();
}
});
Tidak ada satu pun baris yang mendaftarkan tombol baru. Fungsi yang menambah item cukup menyisipkan HTML, dan interaksinya langsung aktif. Pendekatan yang sama juga berguna saat memakai validasi form dengan jQuery pada baris input yang jumlahnya bisa bertambah, karena aturan validasi bisa dipanggil ulang lewat satu handler di container.
Untuk daftar yang panjangnya ratusan, basis kode yang lebih besar biasanya beralih ke pola berbasis komponen. Selama masih memakai jQuery, delegation tetap cara paling ringkas untuk menangani interaksi yang muncul dan hilang.
Ringkasan
Event delegation bekerja dengan memasang satu listener di induk yang stabil, lalu membiarkan jQuery memfilter target berdasarkan selector. Ini menghilangkan kebutuhan mendaftarkan ulang handler setiap kali render, mengurangi jumlah listener, dan menjaga kode interaksi terpisah dari kode yang membangun DOM. Yang perlu dijaga cuma tiga hal: pilih induk terdekat, tulis selector sesederhana mungkin, dan pasang handler hanya sekali.
Pertanyaan yang Sering Diajukan
Apakah event delegation memperlambat klik karena event harus naik ke atas?
Biaya bubbling sangat kecil dan biasanya tidak terasa. Yang lebih berpengaruh adalah jumlah listener di halaman. Satu listener delegasi menggantikan puluhan listener individual, dan itu justru menghemat kerja browser pada daftar yang besar.
Bisakah delegation dipakai untuk event selain click?
Bisa, selama event tersebut melakukan bubbling. Event seperti change, input, mouseover, dan keydown aman didelegasikan. Hindari event yang tidak naik, misalnya focus dan blur, kecuali kamu memakai varian focusin dan focusout.
Apakah perlu melepas listener saat elemen dihapus?
Kalau listener dipasang di induk yang masih hidup, tidak. Handler di induk tetap valid dan tidak menyimpan referensi ke elemen anak. Situasi berbeda kalau kamu mengikat data atau plugin ke elemen anak secara langsung. Pada elemen yang benar-benar dibuang dari DOM, bersihkan dulu sebelum dihapus agar tidak meninggalkan sisa di memori.