Event Delegasi
Teknik efisien menangani event banyak elemen dengan satu listener di parent memakai event bubbling.
Satu listener untuk banyak elemen
Bayangkan list dengan 100 item, tiap item butuh handler klik. Memasang 100 listener boros memori dan merepotkan. Event delegasi memasang SATU listener di parent, lalu mengenali item mana yang diklik lewat e.target.
Cara kerja: event bubbling
Event di DOM "menggelembung" ke atas: klik pada li akan memicu handler di li, lalu ul, lalu body, sampai document. Delegasi memanfaatkan ini:
let ul = document.querySelector("ul");
ul.addEventListener("click", (e) => {
// pastikan yang diklik adalah li, bukan ul-nya
let li = e.target.closest("li");
if (!li) return;
console.log("Klik: " + li.textContent);
li.classList.toggle("selesai");
});Satu listener menangani semua li, sekarang dan nanti.
Keunggulan untuk elemen dinamis
Elemen yang ditambahkan belakangan otomatis ikut ter-cover karena listener ada di parent yang sudah ada sejak awal:
let ul = document.querySelector("ul");
ul.addEventListener("click", (e) => {
let tombolHapus = e.target.closest(".hapus");
if (tombolHapus) {
tombolHapus.closest("li").remove();
}
});
// item baru yang ditambahkan nanti tetap bisa dihapus
let li = document.createElement("li");
li.innerHTML = 'Belajar delegasi <button class="hapus">x</button>';
ul.appendChild(li);Tanpa delegasi, kamu harus memasang listener baru setiap kali menambah item. Itu rawan lupa dan bocor memori.
Kapan tidak cocok?
Delegasi kurang pas untuk event yang tidak bubble (focus, blur, mouseenter tidak menggelembung) atau saat tiap elemen butuh perilaku yang sangat berbeda. Untuk kasus itu, listener langsung lebih jelas.
Catatan teknis: Selalu validasi e.target dengan closest() sebelum dipakai. Klik bisa jatuh di elemen anak yang tidak terduga (misalnya span di dalam button). Pola if (!el) return di awal handler delegasi mencegah error dan perilaku aneh.
Studi kasus: todo list dinamis
Delegasi membuat todo list yang itemnya bisa tambah dan hapus tanpa pasang listener baru:
let ul = document.querySelector("#todo");
ul.addEventListener("click", (e) => {
let tombolHapus = e.target.closest(".hapus");
if (tombolHapus) {
tombolHapus.closest("li").remove();
return;
}
let item = e.target.closest("li");
if (item) item.classList.toggle("selesai");
});
function tambahTodo(teks) {
let li = document.createElement("li");
li.innerHTML = '<span></span><button class="hapus">x</button>';
li.querySelector("span").textContent = teks; // aman dari XSS
ul.appendChild(li); // otomatis ter-cover listener!
}Satu listener menangani klik selesai dan hapus untuk semua item, termasuk yang belum ada saat halaman dimuat. Perhatikan innerHTML hanya untuk struktur tetap, teks pengguna tetap lewat textContent.
Kesimpulan
Delegasi = satu listener di parent + e.target.closest() untuk identifikasi. Ini pola wajib untuk list dinamis, tabel, dan menu. Lebih hemat, lebih rapi, dan otomatis menangani elemen baru.