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:
<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>// 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):
type: click
target: BUTTON "Beli Sekarang"
timeStamp: number
target: #info
currentTarget: #panel
defaultPrevented: trueMari kita telaah baris-baris pentingnya:
- Baris 7 (
function (event)): parametereventdiisi browser dengan object event. Nama parameternya bebas —ejuga lazim — tetapi posisinya tidak: object event selalu argumen pertama. - Baris 8 (
event.type): propertitypeberisi nama event sebagai string, yaitu"click". Satu handler bisa melayani beberapa jenis event sekaligus dan membedakannya lewat properti ini. - Baris 9 (
event.target):targetmenunjuk elemen tempat peristiwa berasal — di sini elemen<button>. PropertitagNamemengembalikan"BUTTON"dalam huruf besar, dantextContentmemberi isi teksnya. - Baris 10 (
typeof event.timeStamp):timeStampberisi waktu peristiwa dalam milidetik sejak halaman dimuat. Nilainya berbeda-beda di setiap percobaan, karena itu contoh ini memeriksa tipe datanya saja — hasilnya selalu"number". - Baris 14–16 (listener pada
#panel): klik terjadi pada<span id="info">, tetapi listener terpasang pada<div id="panel">yang membungkusnya. Baristarget: #infodancurrentTarget: #panelmenunjukkan dua alamat berbeda: asal peristiwa versus elemen tempat listener terdaftar. - Baris 20–22 (
preventDefault): methodpreventDefault()membatalkan perilaku bawaan peristiwa. Untuk tautan, perilaku bawaannya adalah berpindah halaman. Setelah dipanggil, propertidefaultPreventedbernilaitrue— 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:
| Peristiwa | Perilaku bawaan browser |
|---|---|
klik pada <a href> | berpindah ke alamat tautan |
klik tombol type="submit" | mengirim form |
Ctrl+S pada halaman | membuka dialog simpan halaman |
| klik pada kotak centang | mengganti 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):
Handler #senyap berjalan, perambatan dihentikan
Handler #bersuara berjalan
Handler #panel berjalan- Baris 6–8 (listener panel): handler ini seharusnya menerima setiap klik di dalam panel.
- Baris 10–13 (tombol senyap):
stopPropagation()menghentikan perjalanan event, sehingga handler panel tidak pernah berjalan — hanya satu baris keluaran untuk tombol pertama. - 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// 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// 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");
});