Event

addEventListener, objek event, bubbling, dan event delegation.

Daftar isi

Sebuah event adalah sinyal yang dipancarkan browser ketika sesuatu terjadi pada halaman, misalnya tombol diklik, teks diketik, atau form dikirim. Topik ini memasang pendengar event dengan addEventListener, membaca objek event yang dikirimkan bersamanya, dan memahami alur bubbling yang membawa event naik melalui pohon DOM. Alur itu kemudian dimanfaatkan untuk event delegation, yaitu satu listener yang melayani banyak elemen sekaligus.

Memasang listener dengan addEventListener#

Metode addEventListener() dipasang pada satu elemen dan menerima dua argumen utama, yaitu nama event dan fungsi handler yang dipanggil setiap event itu terjadi. Nama event ditulis sebagai string tanpa awalan, contohnya "click", "input", "submit", dan "keydown". Satu elemen boleh memakai beberapa listener untuk event yang sama, dan semuanya berjalan sesuai urutan pemasangannya.

js Contoh: memasang listener klik pada sebuah tombol
// tidak-dijalankan
const tombol = document.querySelector("#simpan");

tombol.addEventListener("click", () => {
  console.log("Tombol simpan ditekan");
});

Fungsi handler boleh ditulis sebagai arrow function atau fungsi biasa, dan keduanya menerima objek event dari pemanggilnya. Penulisan nama event harus persis, "Click" dengan huruf kapital tidak akan pernah terpanggil karena tidak ada event dengan nama itu. Ketika handler tidak lagi diperlukan, removeEventListener() melepasnya dengan syarat fungsi yang diberikan sama persis dengan yang dipasang. Karena itu, handler yang akan dilepas sebaiknya disimpan di dalam variabel.

Event yang bisa dipantau tidak berhenti pada klik. Event keydown beserta properti event.key membuka pintu pintasan keyboard, misalnya memeriksa tombol Enter pada kolom pencarian, sedangkan event input melaporkan setiap perubahan isian seketika. Daftar lengkap tipe event milik browser memang panjang, tetapi cara memasangnya selalu sama lewat addEventListener, sehingga menambah jenis respons baru hanya berarti mengganti nama eventnya.

Objek event dan properti target#

Setiap pemanggilan handler disertai satu objek event yang berisi keterangan kejadiannya. Properti type menyebut jenis eventnya, sedangkan target menunjuk elemen tempat event itu terjadi. Pada handler klik, event.target adalah elemen terdalam yang benar-benar diklik, dan elemen itu bisa saja berbeda dari pemasang listener ketika ada elemen bersarang di dalamnya.

js Contoh: membaca type dan target dari objek event
// tidak-dijalankan
daftar.addEventListener("click", (event) => {
  console.log(event.type);   // click
  console.log(event.target); // elemen terdalam yang diklik
});

Objek event juga membawa metode untuk mengubah perilaku bawaan browser. preventDefault() membatalkan aksi bawaannya, misalnya memuat ulang halaman ketika form dikirim, dan pembahasan lengkapnya ada pada topik form. Metode stopPropagation() menghentikan perjalanan event yang akan dibahas pada bagian berikutnya.

Bubbling: event naik melalui pohon#

Klik pada elemen terdalam tidak berhenti di elemen itu saja. Setelah handler pada target diberi kesempatan, event yang dapat membubung naik satu tingkat demi satu tingkat sampai document, dan perilaku ini disebut bubbling karena menyerupai gelembung yang naik ke permukaan. Diagram berikut mengikuti satu klik pada li di dalam daftar, dari target sampai ujung atas pohon.

documenthtmlbodyulli (target)2 ul3 body4 html5 document1 klik terjadi di liHandler yang dipasang ditingkat mana pun di sepanjangjalur ini menerima event,termasuk handler tunggal didocument untuk semua item.
Alur bubbling satu klik pada li, dari target naik ke ul, body, html, dan berakhir di document

Karena event melintasi semua leluhurnya, listener boleh dipasang di tingkat mana pun sepanjang jalur itu. Listener pada ul menerima klik yang terjadi pada li anaknya, dan listener pada document menerima klik dari seluruh halaman. Inilah dasar event delegation, satu listener dipasang pada induk, lalu handler memeriksa event.target untuk menentukan item mana yang benar-benar diklik.

js Contoh: delegasi klik pada induk daftar
// tidak-dijalankan
daftar.addEventListener("click", (event) => {
  const item = event.target.closest("li");

  if (!item) return; // klik di luar item, abaikan
  console.log("Item diklik: " + item.textContent);
});

Metode closest() mencari leluhur terdekat yang cocok dengan selektor, dimulai dari event.target itu sendiri. Pencarian ini menangani kasus li yang berisi elemen lain, misalnya teks tebal atau gambar, karena target kliknya bisa elemen terdalam, sedangkan yang dibutuhkan adalah baris li pembungkusnya.

Perjalanan bubbling juga dapat dipotong dengan event.stopPropagation(), dan pemakaiannya perlu diukur. Listener lain di sepanjang jalur tidak pernah tahu event itu pernah terjadi. Akibat ini jarang diinginkan karena handler induk sering juga mengandalkan event yang sama. Hentikan bubbling hanya bila alurnya memang harus dipotong, bukan sekadar untuk merapikan log.

Demo berikut memperagakan delegasi pada daftar belanja. Klik salah satu item, dan log akan mencatat item yang diklik lengkap dengan urutan kejadiannya, lalu tekan tombol Tambah item dan klik juga item barunya. Listener tidak pernah dipasang ulang, item baru langsung terlayani oleh listener induk yang sama, dan inilah bukti bahwa delegasi bekerja untuk isi yang tidak diketahui saat listener ditulis.

Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <title>Demo event delegation pada daftar</title>
  <style>
    body { font-family: "Segoe UI", Arial, sans-serif; margin: 14px; font-size: 14px; color: #1f2937; }
    button { padding: 6px 12px; margin-bottom: 10px; cursor: pointer; }
    #daftar { background: #f1f5f9; border: 1px solid #94a3b8; border-radius: 8px; padding: 10px 10px 10px 34px; max-width: 300px; margin-bottom: 4px; }
    #daftar li { padding: 3px 6px; cursor: pointer; }
    #daftar li:hover { background: #e0e7ff; }
    #log { font-size: 13px; padding-left: 20px; max-width: 360px; }
  </style>
</head>
<body>
  <p>Klik item mana pun pada daftar, lalu tambahkan item baru dan klik juga:</p>
  <ul id="daftar">
    <li>Gula pasir</li>
    <li>Tepung terigu</li>
    <li>Minyak goreng</li>
  </ul>
  <button id="tambah" type="button">Tambah item</button>
  <ol id="log"></ol>
  <script src="index.js"></script>

<script>
// Demo event delegation: satu listener induk melayani semua item, termasuk item baru.

const daftar = document.querySelector("#daftar");
const log = document.querySelector("#log");
let urutan = 0;

function catat(pesan) {
  urutan += 1;
  const baris = document.createElement("li");
  baris.textContent = urutan + ". " + pesan;
  log.append(baris);
}

daftar.addEventListener("click", (event) => {
  const item = event.target.closest("li");
  if (!item) return;
  catat("klik pada \"" + item.textContent + "\"");
});

document.querySelector("#tambah").addEventListener("click", () => {
  const item = document.createElement("li");
  item.textContent = "Bahan tambahan " + (daftar.children.length + 1);
  daftar.append(item);
  catat("item baru disisipkan, tanpa listener baru");
});

</script>
<script>
// Demo event delegation: satu listener induk melayani semua item, termasuk item baru.

const daftar = document.querySelector("#daftar");
const log = document.querySelector("#log");
let urutan = 0;

function catat(pesan) {
  urutan += 1;
  const baris = document.createElement("li");
  baris.textContent = urutan + ". " + pesan;
  log.append(baris);
}

daftar.addEventListener("click", (event) => {
  const item = event.target.closest("li");
  if (!item) return;
  catat("klik pada \"" + item.textContent + "\"");
});

document.querySelector("#tambah").addEventListener("click", () => {
  const item = document.createElement("li");
  item.textContent = "Bahan tambahan " + (daftar.children.length + 1);
  daftar.append(item);
  catat("item baru disisipkan, tanpa listener baru");
});

</script></body>
</html>

Ringkasan#

  • addEventListener(nama, handler) memasang fungsi respons pada satu elemen, dengan nama event ditulis persis seperti "click" atau "input".
  • Setiap pemanggilan handler menerima objek event yang menyimpan type dan target, dan currentTarget membedakan pemasang listener dari asal event.
  • Event yang dapat membubung naik dari target melalui seluruh leluhurnya sampai document, sehingga listener di tingkat mana pun dapat menangkapnya.
  • Event delegation menumpang alur bubbling, satu listener pada induk ditambah event.target.closest() melayani semua anak, termasuk anak yang lahir belakangan.

Referensi#