Siti membangun halaman detail produk untuk toko Budi. Handler-nya butuh tahu lebih banyak daripada sekadar “ada yang diklik”: elemen mana yang persis diklik, peristiwanya jenis apa, apakah klik itu pada tautan yang harus dibatalkan navigasinya, dan sebagainya. Kalau setiap handler harus menebak dari kondisi halaman, kodenya akan penuh dugaan yang mudah salah.

Browser menyelesaikan masalah itu dengan cara yang rapi: setiap kali event terjadi, browser membuat sebuah object event yang berisi laporan lengkap peristiwa tersebut, lalu menyerahkannya sebagai argumen pertama function handler. Lewat object inilah kita membaca type dan target, membandingkan target dengan currentTarget, membatalkan perilaku bawaan lewat preventDefault(), dan — sekilas di bagian akhir — menghentikan perambatan dengan stopPropagation(). Chapter ini membedah semuanya lewat contoh yang kita jalankan sendiri.

Satu Object Event untuk Setiap Handler #

Mekanismenya bekerja seperti ini: saat peristiwa terjadi, browser mengemas detailnya ke dalam object — turunan dari Event seperti MouseEvent untuk klik — kemudian memanggil setiap listener yang cocok dengan menyisipkan object itu sebagai argumen pertama. Setelah semua handler selesai, object event dibuang dari memori. Artinya, object event adalah “dokumen laporan” sekali pakai: murni untuk dibaca, bukan disimpan untuk dipakai nanti.

Struktur HTML dan berkas contoh kita mencakup tiga skenario sekaligus — tombol beli, panel yang memuat teks, dan sebuah tautan:

html
<div id="kartu"><button id="beli">Beli Sekarang</button></div>
<div id="panel"><span id="info">Detail Produk</span></div>
<a id="tautan" href="https://example.com/katalog">Lihat katalog</a>
js info-event.js
// info-event.js — properti penting pada object event
const tombolBeli = document.querySelector("#beli");
const panel = document.querySelector("#panel");
const tautan = document.querySelector("#tautan");

// --- 1. properti dasar: type, target, timeStamp ---
tombolBeli.addEventListener("click", function (event) {
  console.log(`type: ${event.type}`);
  console.log(`target: ${event.target.tagName} "${event.target.textContent}"`);
  console.log(`timeStamp: ${typeof event.timeStamp}`);
});

// --- 2. target vs currentTarget pada elemen induk ---
panel.addEventListener("click", function (event) {
  console.log(`target: #${event.target.id}`);
  console.log(`currentTarget: #${event.currentTarget.id}`);
});

// --- 3. preventDefault membatalkan perilaku bawaan ---
tautan.addEventListener("click", function (event) {
  event.preventDefault();
  console.log(`defaultPrevented: ${event.defaultPrevented}`);
});

Untuk mengujinya, kita mensimulasikan tiga aksi pengguna secara berurutan: menekan tombol #beli, menekan teks #info di dalam panel, lalu menekan tautan #tautan. Keluaran berikut dicatat dari eksekusi nyata berkas ini di lingkungan DOM jsdom — sama persis dengan urutan console browser bila kita mengulang ketiga aksi itu.

Output yang Dihasilkan (console browser):

text
type: click
target: BUTTON "Beli Sekarang"
timeStamp: number
target: #info
currentTarget: #panel
defaultPrevented: true

Mari kita telaah baris-baris pentingnya:

  1. Baris 7 (function (event)): parameter event diisi browser dengan object event. Nama parameternya bebas — e juga lazim — tetapi posisinya tidak: object event selalu argumen pertama.
  2. Baris 8 (event.type): properti type berisi nama event sebagai string, yaitu "click". Satu handler bisa melayani beberapa jenis event sekaligus dan membedakannya lewat properti ini.
  3. Baris 9 (event.target): target menunjuk elemen tempat peristiwa berasal — di sini elemen <button>. Properti tagName mengembalikan "BUTTON" dalam huruf besar, dan textContent memberi isi teksnya.
  4. Baris 10 (typeof event.timeStamp): timeStamp berisi waktu peristiwa dalam milidetik sejak halaman dimuat. Nilainya berbeda-beda di setiap percobaan, karena itu contoh ini memeriksa tipe datanya saja — hasilnya selalu "number".
  5. Baris 14–16 (listener pada #panel): klik terjadi pada <span id="info">, tetapi listener terpasang pada <div id="panel"> yang membungkusnya. Baris target: #info dan currentTarget: #panel menunjukkan dua alamat berbeda: asal peristiwa versus elemen tempat listener terdaftar.
  6. Baris 20–22 (preventDefault): method preventDefault() membatalkan perilaku bawaan peristiwa. Untuk tautan, perilaku bawaannya adalah berpindah halaman. Setelah dipanggil, properti defaultPrevented bernilai true — penanda yang bisa dibaca handler lain.

Properti di atas baru sebagian dari isi object event. Saat kita mulai menangani jenis peristiwa tertentu, isi laporannya melengkung mengikuti kebutuhan: peristiwa pointer membawa posisi kursor (clientX, clientY), peristiwa keyboard membawa key dan code yang dibahas penuh pada chapter Keyboard Events, sementara peristiwa form membawa value pada targetnya. Satu hal yang tetap di semua jenis: type, target, dan timeStamp selalu ada.

target dan currentTarget: Dua Alamat yang Berbeda #

Perbedaan target dan currentTarget layak diperlambat karena sering tertukar. Properti target selalu menunjuk elemen tempat peristiwa bermula — elemen persis yang diklik pengguna. Properti currentTarget menunjuk elemen yang saat ini menjalankan handler — elemen tempat addEventListener dipanggil. Pada listener tombol #beli, keduanya sama karena listener terpasang tepat di elemen yang diklik; pada listener panel, keduanya berbeda karena klik berasal dari anaknya.

Properti currentTarget juga satu-satunya cara aman mengetahui elemen pemilik handler di dalam function yang dipakai ulang untuk beberapa elemen. Variabel panel di luar memang menunjuk elemen yang sama pada contoh di atas, tetapi bila function handler-nya dipasang di banyak elemen, yang valid adalah event.currentTarget, bukan variabel luar.

Panduan singkatnya: gunakan event.target ketika yang dibutuhkan adalah asal peristiwa — elemen yang persis disentuh pengguna. Gunakan event.currentTarget ketika yang dibutuhkan adalah pemilik handler — elemen tempat kita memasang listener. Dan bila target harus dicocokkan dengan selector tertentu, method matches() dan closest() yang dibahas pada chapter Event Delegation menjadi alat lanjutannya.

preventDefault: Membatalkan Perilaku Bawaan #

Banyak peristiwa punya perilaku bawaan yang langsung dijalankan browser: klik pada <a> berpindah halaman, klik tombol type="submit" mengirim form, tekan spasi menggulir halaman. Method preventDefault() pada object event menyuruh browser membatalkan perilaku itu — tanpa mengganggu listener lain. Halaman tidak berpindah, dan kode kita yang menentukan reaksinya.

Pada baris 20–23 berkas contoh, klik tautan #tautan tetap terdeteksi sebagai event click, tetapi navigasi ke https://example.com/katalog tidak terjadi karena preventDefault() dipanggil lebih dulu. Pola ini adalah fondasi validasi form: kita menahan pengiriman data, memeriksa isinya, lalu memutuskan mengirim atau menolak. Perlu diingat, pembatalan hanya berhasil bila peristiwanya memang bisa dibatalkan — browser menandainya lewat properti cancelable pada object event.

Perilaku bawaan yang paling sering dibatalkan antara lain:

PeristiwaPerilaku bawaan browser
klik pada <a href>berpindah ke alamat tautan
klik tombol type="submit"mengirim form
Ctrl+S pada halamanmembuka dialog simpan halaman
klik pada kotak centangmengganti status tercentang

Satu batasan yang perlu diingat: preventDefault() hanya membatalkan. Method ini tidak memberi tahu listener lain, tidak mengubah isi object event selain defaultPrevented, dan tidak menghentikan perjalanan peristiwa — tugas yang berbeda milik stopPropagation().

stopPropagation: Pengantar #

Selain membatalkan perilaku bawaan, object event juga menyediakan method stopPropagation() untuk menghentikan perjalanan event ke elemen-elemen lain. Kita hanya mengenalnya sekilas di sini karena perambatan sendiri belum kita bahas. Berkas berikut memasang dua tombol di dalam satu panel; tombol pertama menghentikan perambatan, tombol kedua tidak:

// stop-propagasi.js — menghentikan perambatan event ke elemen induk
const panel = document.querySelector("#panel");
const tombolSenyap = document.querySelector("#senyap");
const tombolBersuara = document.querySelector("#bersuara");

panel.addEventListener("click", function () {
  console.log("Handler #panel berjalan");
});

tombolSenyap.addEventListener("click", function (event) {
  event.stopPropagation();
  console.log("Handler #senyap berjalan, perambatan dihentikan");
});

tombolBersuara.addEventListener("click", function () {
  console.log("Handler #bersuara berjalan");
});

Kita menekan #senyap terlebih dahulu, lalu #bersuara:

Output yang Dihasilkan (console browser):

text
Handler #senyap berjalan, perambatan dihentikan
Handler #bersuara berjalan
Handler #panel berjalan
  1. Baris 6–8 (listener panel): handler ini seharusnya menerima setiap klik di dalam panel.
  2. Baris 10–13 (tombol senyap): stopPropagation() menghentikan perjalanan event, sehingga handler panel tidak pernah berjalan — hanya satu baris keluaran untuk tombol pertama.
  3. Baris 15–17 (tombol bersuara): tanpa stopPropagation(), event naik seperti biasa: handler tombol berjalan dulu, lalu handler panel.

Sekarang pertanyaannya muncul dengan sendirinya: bagaimana tepatnya event berjalan dari elemen yang diklik sampai ke panel induknya, dan dalam urutan apa? Chapter berikutnya membahas perambatan itu secara sengaja dan teruji.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

info-event.js

Unduh berkas
javascript
// info-event.js — properti penting pada object event
const tombolBeli = document.querySelector("#beli");
const panel = document.querySelector("#panel");
const tautan = document.querySelector("#tautan");

// --- 1. properti dasar: type, target, timeStamp ---
tombolBeli.addEventListener("click", function (event) {
  console.log(`type: ${event.type}`);
  console.log(`target: ${event.target.tagName} "${event.target.textContent}"`);
  console.log(`timeStamp: ${typeof event.timeStamp}`);
});

// --- 2. target vs currentTarget pada elemen induk ---
panel.addEventListener("click", function (event) {
  console.log(`target: #${event.target.id}`);
  console.log(`currentTarget: #${event.currentTarget.id}`);
});

// --- 3. preventDefault membatalkan perilaku bawaan ---
tautan.addEventListener("click", function (event) {
  event.preventDefault();
  console.log(`defaultPrevented: ${event.defaultPrevented}`);
});

stop-propagasi.js

Unduh berkas
javascript
// stop-propagasi.js — menghentikan perambatan event ke elemen induk
const panel = document.querySelector("#panel");
const tombolSenyap = document.querySelector("#senyap");
const tombolBersuara = document.querySelector("#bersuara");

panel.addEventListener("click", function () {
  console.log("Handler #panel berjalan");
});

tombolSenyap.addEventListener("click", function (event) {
  event.stopPropagation();
  console.log("Handler #senyap berjalan, perambatan dihentikan");
});

tombolBersuara.addEventListener("click", function () {
  console.log("Handler #bersuara berjalan");
});