Browser: HTML, CSS, DOM, dan Aksesibilitas · 7/8
Event, bubbling, dan delegation
Bagaimana sebuah klik berjalan melewati halamanmu, dan kenapa itu penting nanti di React.
Baca 25 menit
Setelah pelajaran ini kamu bisa
- Menjelaskan tiga fase yang dilalui sebuah event
- Menjelaskan bedanya bubbling dan capturing
- Memakai event delegation, dan menjelaskan kenapa ia lebih skalabel
- Memilih dengan tepat antara `preventDefault` dan `stopPropagation`
Klik sebuah button di dalam kartu di dalam list, dan klik itu tidak hanya terjadi pada button-nya. Ia berjalan — turun dari dokumen ke button, lalu kembali naik. Memahami perjalanan itu menjelaskan delegation, dan menjelaskan seluruh sistem event React.
| Fase | Arah | Listener yang aktif |
|---|---|---|
| 1. Capture | document → target | Hanya yang didaftarkan dengan capture: true |
| 2. Target | di elemennya sendiri | Semua listener pada target-nya |
| 3. Bubble | target → document | Yang biasa — ini default-nya |
Propagasi lengkap, diimplementasikan. Ubah `stopAt` untuk melihat apa yang sebenarnya dihentikan stopPropagation.
Hasil
Tekan Jalankan untuk melihat hasilnya.
Ini jalan di browser kamu, di dalam sandbox. Apa pun yang kamu tulis di sini tidak bisa merusak situs.
Perhatikan listener bubble terakhir: close-menus-on-outside-click di document. Pola itu ada di mana-mana, dan itu sebabnya satu stopPropagation() yang sembarangan merusak fitur yang tidak berhubungan — listener di document-nya sama sekali tidak mendengar klik itu.
preventDefault versus stopPropagation
| Method | Menghentikan | Dipakai untuk |
|---|---|---|
preventDefault() | Reaksi bawaan browser | Mencegah form ter-submit atau link berpindah halaman |
stopPropagation() | Event berjalan lebih jauh | Mencegah handler leluhurnya ikut aktif |
// Handle a form in JavaScript instead of a full page reload.
form.addEventListener("submit", (event) => {
event.preventDefault(); // the browser would navigate otherwise
save(new FormData(form));
});
// A delete button inside a clickable card: do not also open the card.
deleteButton.addEventListener("click", (event) => {
event.stopPropagation(); // the card's handler must not fire
remove();
});Delegation
Karena event ber-bubble, kamu bisa memasang satu listener di container alih-alih satu per anak. event.target memberitahumu apa yang sebenarnya diklik.
// One listener for a list of any length, including rows added later.
document.querySelector(".venues").addEventListener("click", (event) => {
// closest() walks up from the target looking for a match.
const button = event.target.closest("[data-action]");
if (!button) return; // clicked the gap between items
const card = button.closest(".card");
const action = button.dataset.action;
if (action === "delete") remove(card.dataset.venueId);
if (action === "favourite") favourite(card.dataset.venueId);
});event.target adalah yang diklik; event.currentTarget adalah elemen yang listener-nya sedang jalan. Menukar keduanya adalah bug yang umum.Pencocokan delegation, dengan `closest()` yang diimplementasikan. Inilah sebabnya satu listener bisa melayani seribu baris.
Hasil
Tekan Jalankan untuk melihat hasilnya.
Ini jalan di browser kamu, di dalam sandbox. Apa pun yang kamu tulis di sini tidak bisa merusak situs.
Tugas praktik
Di console browser, jalankan ini lalu klik di mana pun di halaman: document.addEventListener("click", e => console.log(e.target.tagName, "->", e.currentTarget === document)). Klik sebuah button, lalu klik teks di dalamnya. Perhatikan target-nya berubah tapi currentTarget-nya selalu document. Muat ulang untuk menghapus listener-nya.
Hasil yang diharapkan
Nama tag target berbeda-beda, currentTarget selalu true.
Cek pemahaman
Pilih jawaban dulu