Budi baru saja merapikan halaman katalog toko bukunya. Tombol “Tambah ke Keranjang” sudah tergambar rapi, kartu produk tersusun simetris, dan warnanya pas dengan desain. Masalahnya satu: tombol itu ditekan berapa kali pun, tidak terjadi apa-apa. HTML dan CSS memang tidak punya cara untuk bereaksi terhadap interaksi — keduanya hanya bertugas menggambar tampilan. Agar tombol bereaksi, kita butuh JavaScript yang mendengarkan peristiwa (event) dan menjalankan kode sebagai jawabannya.

Browser mengabari kode kita setiap kali sesuatu terjadi: tombol ditekan, huruf diketik, form dikirim, pointer bergerak. Saluran pengabaran itu berjalan lewat event listener — function yang kita daftarkan pada sebuah elemen, lalu dijalankan browser ketika event yang cocok datang. Chapter ini membahas cara mendaftarkan listener dengan addEventListener, perilaku saat satu elemen punya banyak listener, cara melepas listener dengan removeEventListener, serta opsi once untuk listener yang cukup berjalan satu kali.

Mendaftarkan Reaksi dengan addEventListener #

Aturannya langsung: setiap elemen memiliki method addEventListener(type, function). Argumen pertama adalah nama event sebagai string, argumen kedua adalah function yang dijalankan browser ketika event tersebut terjadi pada elemen itu. Jenis event yang paling sering dipakai antara lain:

Jenis EventMuncul ketika
clickelemen ditekan dengan mouse atau sentuhan
inputnilai kolom form berubah
changeperubahan nilai selesai dan dikomit
submitform dikirim
keydown, keyuptombol keyboard ditekan atau dilepas
pointerdown, pointeruppointer (mouse, jari, stylus) ditekan atau dilepas
mouseoverpointer masuk ke area elemen

Tempat pendaftarannya juga tidak terbatas pada tombol. Listener bisa dipasang pada elemen apa pun, termasuk document untuk menangkap ketikan di mana pun pada halaman (seperti yang kita pakai pada chapter Keyboard Events) dan window untuk peristiwa tingkat halaman seperti scroll dan resize. Prinsipnya selalu sama: pilih elemen yang paling menggambarkan di mana peristiwa itu menarik bagi kode kita.

Berkas berikut memasang listener di halaman toko Budi. Struktur HTML yang kita pakai hanya dua tombol:

html
<button id="sapa">Sapa Pengunjung</button>
<button id="promo">Klaim Promo</button>
// listener-dasar.js — mendaftarkan dan menjalankan event listener
const tombolSapa = document.querySelector("#sapa");
const tombolPromo = document.querySelector("#promo");

// --- 1. satu listener pada satu elemen ---
function sapaTamu() {
  console.log("Halo, Siti! Selamat datang di Toko Buku Maya.");
}
tombolSapa.addEventListener("click", sapaTamu);

// --- 2. dua listener pada elemen yang sama ---
tombolSapa.addEventListener("click", function catatKunjungan() {
  console.log("Kunjungan dicatat ke log toko.");
});

// --- 3. listener sekali pakai dengan once ---
tombolPromo.addEventListener("click", function () {
  console.log("Kode promo DISKON20 dikirim ke email Siti.");
}, { once: true });

Kode di atas belum mencetak apa pun saat halaman dibuka — listener baru terdaftar, belum dipanggil. Untuk mengujinya, kita mensimulasikan interaksi pengguna: tombol #sapa ditekan dua kali, lalu tombol #promo juga ditekan dua kali. Keluaran berikut dicatat dari eksekusi nyata berkas ini di lingkungan DOM jsdom; console Chrome maupun Firefox menghasilkan deretan yang sama.

Output yang Dihasilkan (console browser):

text
Halo, Siti! Selamat datang di Toko Buku Maya.
Kunjungan dicatat ke log toko.
Halo, Siti! Selamat datang di Toko Buku Maya.
Kunjungan dicatat ke log toko.
Kode promo DISKON20 dikirim ke email Siti.

Mari kita telaah baris-baris pentingnya:

  1. Baris 2–3 (document.querySelector(...)): kedua tombol dicari lebih dulu supaya bisa dirujuk. Listener selalu dipasang pada object elemen, bukan pada string selector.
  2. Baris 6–9 (function sapaTamu dan pendaftarannya): addEventListener("click", sapaTamu) mengikat sapaTamu ke event click pada tombol. Perhatikan bahwa kita mengirim function-nya, bukan memanggilnya — sapaTamu tanpa tanda kurung, karena browser yang akan memanggilnya nanti.
  3. Baris 12–14 (listener kedua): elemen yang sama menerima listener click lagi. Kedua klik pada #sapa menghasilkan dua pasang keluaran: pesan sapaan dan catatan kunjungan, selalu berurutan sesuai urutan pendaftaran.
  4. Baris 17–19 (opsi once: true): argumen ketiga berupa object opsi. Dengan once: true, listener dilepas otomatis setelah satu kali berjalan. Dua klik pada #promo hanya menghasilkan satu baris keluaran karena klik kedua tidak lagi menemukan listener.

Satu Elemen, Banyak Listener #

Keluaran di atas memperlihatkan aturan penting: listener tidak menimpa satu sama lain. Browser menyimpan seluruh listener yang terdaftar pada satu jenis event dalam sebuah daftar, lalu memanggil semuanya secara berurutan setiap event datang. Inilah yang membuat satu tombol bisa melayani dua kepentingan sekaligus — satu listener mengubah tampilan untuk pengunjung, listener lain mencatat aktivitas untuk pemilik toko — tanpa keduanya perlu saling mengetahui.

Pola ini juga menjelaskan mengapa addEventListener jauh lebih disarankan daripada properti lama seperti onclick: penugasan onclick hanya menampung satu function, dan function baru menggantikan yang lama. Dengan addEventListener, setiap pendaftaran bertambah, bukan mengganti.

Urutan pendaftaran pun menjadi kontrak yang perlu dijaga. Bila dua kepentingan saling bergantung — listener kedua harus berjalan setelah listener pertama selesai — gabungkan keduanya dalam satu function, karena urutan antar listener tidak tertulis di mana pun kecuali pada urutan baris pendaftarannya. Listener yang berdiri sendiri, tanpa ketergantungan pada pendengar lain, membuat urutan berhenti menjadi hal yang perlu dipikirkan.

Kapan Event Sebenarnya Dikirim #

Pada halaman sungguhan, sumber event adalah interaksi pengunjung: browser mengamati pergerakan mouse dan penekanan tombol, lalu mengirim peristiwa yang sesuai ke elemen yang bersangkutan. Namun ada sumber kedua yang penting diketahui: kode kita sendiri juga bisa memicu peristiwa. Method el.click() mensimulasikan klik pada satu elemen, dan chapter Custom Events di akhir section memperlihatkan bentuk umumnya lewat dispatchEvent.

Sumber kedua inilah yang membuat setiap contoh di section ini bisa diuji tanpa menunggu pengunjung: keluaran pada chapter ini dicatat setelah peristiwa dikirim secara terprogram ke DOM, dan hasilnya identik dengan interaksi sungguhan karena browser memperlakukan keduanya sama.

Melepas Listener dengan removeEventListener #

Listener yang sudah tidak diperlukan sebaiknya dilepas — misalnya ketika widget dihapus dari halaman. Method removeEventListener(type, function) melakukan tugas itu dengan satu syarat ketat: function yang dikirim harus referensi yang sama dengan yang pernah didaftarkan. Berkas berikut menguji syarat tersebut:

// lepas-listener.js — removeEventListener butuh referensi function yang sama
const tombol = document.querySelector("#sapa");

function sapaTamu() {
  console.log("Halo, Siti!");
}

tombol.addEventListener("click", sapaTamu);
tombol.removeEventListener("click", sapaTamu); // referensi sama -> berhasil dilepas

tombol.addEventListener("click", function () {
  console.log("Listener anonim masih terpasang.");
});

tombol.removeEventListener("click", function () { // function baru, referensi berbeda
  console.log("Baris ini tidak pernah dijalankan.");
});

Setelah berkas ini berjalan, kita menekan tombol #sapa dua kali untuk melihat listener mana yang masih hidup:

Output yang Dihasilkan (console browser):

text
Listener anonim masih terpasang.
Listener anonim masih terpasang.

Mari kita bedah:

  1. Baris 8–9 (pasang lalu lepas sapaTamu): sapaTamu didaftarkan lalu langsung dilepas. Karena yang dikirim ke removeEventListener adalah nama function yang sama, pelepasan berhasil — dua klik berikutnya tidak menghasilkan sapaan.
  2. Baris 11–13 (listener anonim): listener ini tetap hidup, terbukti dari dua baris keluaran di atas.
  3. Baris 15–17 (percobaan melepas yang gagal diam-diam): meskipun isinya mirip, function yang dikirim ke removeEventListener adalah object function yang baru dibuat — bukan yang terdaftar di baris 11. Browser mencocokkan identitas referensi, bukan isi kode, sehingga pelepasan ini tidak berdampak apa pun. Tidak ada pesan galat yang mengingatkan kita.

Listener Sekali Pakai dengan once #

Kasus “jalankan sekali, lalu berhenti” muncul sering: dialog promo yang cukup tampil sekali, tombol yang menonaktifkan dirinya setelah diproses. Menulis pola lepas-sendiri (daftar, jalankan, lepas) untuk tiap kasus melelahkan. Opsi once: true memindahkan tugas itu ke browser: setelah function selesai dijalankan satu kali, browser melepasnya otomatis.

Pada berkas listener-dasar.js, baris 17–19 memakai opsi ini. Klik pertama pada #promo mengirim kode promo; klik kedua tidak memicu apa pun karena daftar listener untuk #promo sudah kosong. Bila nanti tombol yang sama perlu bereaksi lagi dengan perilaku baru, kita tinggal mendaftarkan listener berikutnya seperti biasa.

Function handler yang selama ini kita tulis tanpa parameter sebenarnya menerima satu hal dari browser: sebuah object yang berisi laporan lengkap peristiwa — elemen mana yang diklik, jenis eventnya apa, dan kapan. Object itulah bahasan chapter berikutnya.

Berkas Sumber

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

lepas-listener.js

Unduh berkas
javascript
// lepas-listener.js — removeEventListener butuh referensi function yang sama
const tombol = document.querySelector("#sapa");

function sapaTamu() {
  console.log("Halo, Siti!");
}

tombol.addEventListener("click", sapaTamu);
tombol.removeEventListener("click", sapaTamu); // referensi sama -> berhasil dilepas

tombol.addEventListener("click", function () {
  console.log("Listener anonim masih terpasang.");
});

tombol.removeEventListener("click", function () { // function baru, referensi berbeda
  console.log("Baris ini tidak pernah dijalankan.");
});

listener-dasar.js

Unduh berkas
javascript
// listener-dasar.js — mendaftarkan dan menjalankan event listener
const tombolSapa = document.querySelector("#sapa");
const tombolPromo = document.querySelector("#promo");

// --- 1. satu listener pada satu elemen ---
function sapaTamu() {
  console.log("Halo, Siti! Selamat datang di Toko Buku Maya.");
}
tombolSapa.addEventListener("click", sapaTamu);

// --- 2. dua listener pada elemen yang sama ---
tombolSapa.addEventListener("click", function catatKunjungan() {
  console.log("Kunjungan dicatat ke log toko.");
});

// --- 3. listener sekali pakai dengan once ---
tombolPromo.addEventListener("click", function () {
  console.log("Kode promo DISKON20 dikirim ke email Siti.");
}, { once: true });