D
P
0
← Semua artikel Read in English

JavaScript, DOM & Animasi Browser

Satu Inline onclick stopPropagation Diam-diam Merusak Semua Delegated Handler

· · 3 menit baca
Satu Inline onclick stopPropagation Diam-diam Merusak Semua Delegated Handler

Bug ini termasuk yang paling licik, karena gejalanya adalah tidak ada gejala. Sebuah tombol favorit (ikon hati) di kartu produk diklik, lalu tidak terjadi apa-apa. Tidak ada perubahan UI, tidak ada network request, tidak ada error di console. Tombolnya seperti hidup (ada hover state, kursor pointer), tapi mengkliknya seolah membicarakan ke ruang hampa.

Yang bikin makin membingungkan: navigasi kartunya juga tidak jalan, jadi sekilas seperti tombolnya "menangkap" klik dengan benar. Tapi tidak ada satu pun aksi yang dieksekusi.

Struktur HTML-nya begini, dengan tombol hati bersarang di dalam <a> yang membungkus seluruh kartu:

<a href="/product/123" class="card">
  <!-- isi kartu -->
  <button
    class="favorite"
    data-id="123"
    onclick="event.preventDefault(); event.stopPropagation();"
  >

  </button>
</a>

onclick inline itu sengaja ditaruh untuk mencegah kartu navigasi saat hati diklik. Sementara itu, logika simpan favorit yang sebenarnya ditangani oleh delegated handler yang dipasang di document:

document.addEventListener("click", (e) => {
  const btn = e.target.closest(".favorite");
  if (!btn) return;
  e.preventDefault();
  e.stopPropagation();
  saveFavorite(btn.dataset.id); // ini yang tidak pernah jalan
});

Pola yang masuk akal, kan? Inline onclick menghentikan navigasi, handler global menyimpan favorit. Tapi saveFavorite() tidak pernah dipanggil.

Akar masalah: stopPropagation membunuh delegasi

Begini yang sebenarnya terjadi. Inline onclick berjalan di target phase, sebelum event sempat naik (bubble) ke leluhur. Saat handler inline memanggil event.stopPropagation(), ia menghentikan event tepat di situ. Event tidak pernah bubble sampai ke document. Karena delegated handler dipasang di document dan bergantung pada bubbling untuk menerima event, handler itu tidak pernah dipicu. Hasilnya: preventDefault() mencegah navigasi (itu kenapa kartu diam), tapi stopPropagation() juga memutus rantai ke handler global, jadi tidak ada favorit yang tersimpan.

Saya buktikan langsung di DevTools: hapus atribut onclick inline-nya, klik tombolnya lagi, dan langsung jalan. saveFavorite() terpanggil, request terkirim. Itu konfirmasi pasti bahwa si inline onclick lah biang keroknya.

Solusi: buang stopPropagation, sisakan preventDefault

Perbaikannya kecil. Cukup hapus event.stopPropagation() dari inline onclick, sisakan event.preventDefault() saja:

<button
  class="favorite"
  data-id="123"
  onclick="event.preventDefault();"
>

</button>

Kenapa ini aman? Karena delegated handler di document melakukan preventDefault() dan stopPropagation()-nya sendiri begitu ia menerima event. Jadi:

Navigasi tetap terblokir, dan favorit kini tersimpan.

Catatan penutup

Pelajaran intinya: kalau Anda memasang logika lewat delegasi di document, satu stopPropagation di mana pun di jalur bubble bisa mematikannya secara senyap. Kenali jalur event-nya sebelum menebak-nebak.