Idenya
Sebuah halaman web lebih banyak menunggu. Tidak terjadi apa-apa sampai pengguna melakukan sesuatu: mengklik, mengetik, men-submit, menekan tombol. Masing-masing adalah sebuah event, dan JavaScript menjalankan kodemu saat event itu terjadi lewat sebuah event listener. "Saat tombol ini diklik, tambahkan item ke keranjang" adalah kalimat dasar dari setiap halaman interaktif.
Seperti di modul sebelumnya, setiap contoh membangun halaman kecilnya sendiri. button.click() dan dispatchEvent menyimulasikan apa yang akan dilakukan pengguna, sehingga contohnya berjalan tanpa ada yang mengklik.
addEventListener
Halaman yang hanya berubah saat pertama kali dimuat tidak terlalu interaktif. Kamu ingin sesuatu terjadi saat pelanggan melakukan sesuatu: mengklik "Add to cart", mengetik di kotak pencarian, mengirim formulir. Kamu memberi tahu sebuah elemen "saat ini terjadi, jalankan function ini":
document.body.innerHTML = `<button id="add">Add to cart</button> <span id="count">0</span>`;
const button = document.querySelector("#add");
const count = document.querySelector("#count");
button.addEventListener("click", () => {
count.textContent = Number(count.textContent) + 1;
console.log("Cart:", count.textContent);
});
button.click();
button.click();
Cart: 1
Cart: 2
element.addEventListener(type, handler) mendaftarkan function untuk dijalankan setiap kali event itu terjadi pada elemen itu. Oper function-nya sendiri; jangan dipanggil: addEventListener("click", addToCart), bukan addToCart().
Kamu akan melihat atribut onclick="..." di tutorial lama. Hindari: atribut itu mencampur JavaScript ke HTML, hanya mengizinkan satu handler, dan diblokir oleh banyak kebijakan keamanan.
Objek event
Setiap handler menerima objek event yang mendeskripsikan apa yang terjadi:
document.body.innerHTML = `<button class="buy" data-id="7">Buy <strong>now</strong></button>`;
const buy = document.querySelector(".buy");
buy.addEventListener("click", (event) => {
console.log(event.type);
console.log(event.currentTarget.dataset.id); // the element the listener is on
console.log(event.target.tagName); // the element actually clicked
});
buy.querySelector("strong").click();
click
7
STRONG
event.currentTarget adalah elemen tempat kamu memasang listener; event.target adalah elemen terdalam yang benar-benar diklik (di sini, <strong> di dalam tombol). Perbedaannya penting untuk pola delegasi di bawah.
Mengetik: event input
Kotak pencarian yang memperbarui hasilnya sambil kamu mengetik terasa jauh lebih cepat daripada yang mengharuskanmu menekan tombol. Event input terpicu setiap kali nilai field-nya berubah. Dua baris terakhir di sini mensimulasikan seseorang yang mengetik, karena tidak ada keyboard di contoh ini:
document.body.innerHTML = `<input id="search" aria-label="Search"> <p id="echo"></p>`;
const search = document.querySelector("#search");
const echo = document.querySelector("#echo");
search.addEventListener("input", () => {
echo.textContent = `Searching for "${search.value.trim()}"`;
});
search.value = " gayo ";
search.dispatchEvent(new Event("input"));
console.log(echo.textContent);
Searching for "gayo"
input terpicu di setiap perubahan nilai sebuah field (setiap ketukan tombol, tempel, atau hapus); change terpicu saat pengguna menyelesaikan perubahan (meninggalkan field, memilih sebuah opsi). Baca nilai saat ini dari element.value, yang selalu berupa string.
Formulir: submit dan preventDefault
Saat seseorang mengirim formulir, perilaku bawaan browser adalah mengirim datanya ke server lalu memuat halaman baru. Untuk pendaftaran newsletter, itu mengagetkan: kamu lebih suka menampilkan "Terima kasih!" langsung di halaman yang sama. Kamu menangkap event submit lalu menghentikan perilaku bawaannya:
document.body.innerHTML = `
<form id="newsletter">
<input name="email" value="[email protected]" aria-label="Email">
<button>Subscribe</button>
</form>
<p id="message"></p>`;
const form = document.querySelector("#newsletter");
form.addEventListener("submit", (event) => {
event.preventDefault(); // stay on this page
const data = new FormData(form);
document.querySelector("#message").textContent = `Thanks, ${data.get("email")}!`;
console.log("prevented:", event.defaultPrevented);
});
form.requestSubmit();
console.log(document.querySelector("#message").textContent);
prevented: true
Thanks, [email protected]!
- Dengarkan
submitpada formulir, bukanclickpada tombol. Dengan begitu menekan Enter di sebuah field juga berfungsi. - Secara default, men-submit formulir memuat halaman baru.
event.preventDefault()menghentikannya, sehingga JavaScript bisa menangani datanya (memvalidasi, mengirim denganfetch, menampilkan pesan). new FormData(form)mengumpulkan setiap field yang bernama;.get(name)membaca salah satunya.
preventDefault juga berfungsi untuk default lainnya: mengikuti link, mencentang checkbox, menggulir dengan tombol panah.
Satu listener untuk banyak elemen: delegasi
Daftar 50 produk dengan 50 tombol "Add" tidak butuh 50 listener. Event menggelembung (bubble): setelah terpicu pada elemen yang diklik, event itu berjalan naik melewati setiap induknya. Jadi satu listener pada container bisa menangani semuanya:
document.body.innerHTML = `
<ul id="products">
<li>Arabica Gayo <button data-id="1">Add</button></li>
<li>Toraja Kalosi <button data-id="2">Add</button></li>
</ul>`;
const list = document.querySelector("#products");
const added = [];
list.addEventListener("click", (event) => {
const button = event.target.closest("button[data-id]");
if (!button) return; // a click elsewhere in the list
added.push(Number(button.dataset.id));
});
list.querySelectorAll("button")[1].click();
list.querySelector("li").click(); // clicking the text, not a button
list.querySelectorAll("button")[0].click();
console.log(added);
[2, 1]
event.target.closest(selector) berjalan naik dari elemen yang diklik ke kecocokan terdekat (atau null). Delegasi juga berfungsi untuk elemen yang ditambahkan belakangan, seperti produk yang di-render setelah pencarian, yang tidak akan tertangani oleh listener terpisah.
Event keyboard
document.body.innerHTML = `<div id="dialog">Added to cart ✓</div>`;
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
document.querySelector("#dialog").hidden = true;
console.log("Dialog closed");
}
});
document.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape" }));
Dialog closed
event.key berisi nama tombolnya: "Enter", "Escape", "ArrowDown", "a".
el.addEventListener("click" | "input" | "submit" | "keydown", handler); handler mendapat objek event (target, currentTarget, key). Tangani formulir pada submit dengan preventDefault(). Untuk banyak elemen yang serupa, gunakan satu listener yang didelegasikan dengan event.target.closest(...).
Mengapa ini penting untuk kode yang bersih
Jaga handler tetap pendek: baca apa yang terjadi dari event-nya, panggil function bernama jelas yang mengerjakan tugasnya (addToCart(id)), lalu perbarui halamannya. Logika bisnis di dalam handler tidak bisa dipakai ulang atau dites; di dalam function, bisa. Delegasi menjaga jumlah listener tetap konstan sebesar apa pun halamannya.
Rangkuman
addEventListener(type, fn): oper function-nya, jangan panggil. Hindari atributonclick.event.target(yang diklik) vsevent.currentTarget(tempat listener berada).inputuntuk ketikan langsung,changeuntuk perubahan yang sudah selesai;element.valueberupa string.- Formulir: dengarkan
submit, panggilevent.preventDefault(), baca field denganFormData. - Delegasi: satu listener pada induk +
event.target.closest(...). keydowndenganevent.key.