Idenya
Saat browser memuat halaman, ia mengubah HTML menjadi pohon objek yang disebut DOM (Document Object Model). JavaScript bisa membaca dan mengubah pohon itu, dan halamannya langsung diperbarui. Begitulah penghitung keranjang naik, daftar tersaring saat kamu mengetik, atau pesan error muncul di bawah field formulir. Semua hal interaktif di browser bermuara pada "temukan sebuah elemen, ubah elemen itu".
Contoh-contoh di modul ini membangun halaman kecilnya sendiri lebih dulu dengan document.body.innerHTML, sehingga masing-masing bisa berjalan sendiri.
Memilih elemen
Sebelum kamu bisa mengubah apa pun di halaman, kamu harus menemukannya. Ini daftar produk kecil. Baris pertama membangun halamannya (di situs sungguhan, isinya sudah ada di HTML), dan sisanya mencari elemen di dalamnya memakai CSS selector yang sudah kamu kenal:
document.body.innerHTML = `
<h1 id="title">Toko Kita</h1>
<ul class="products">
<li class="product" data-id="1">Arabica Gayo</li>
<li class="product sold-out" data-id="2">Toraja Kalosi</li>
<li class="product" data-id="3">Kintamani</li>
</ul>`;
const title = document.querySelector("#title");
const first = document.querySelector(".product");
const all = document.querySelectorAll(".product");
const missing = document.querySelector(".does-not-exist");
console.log(title.textContent);
console.log(first.textContent, all.length);
console.log(missing);
Toko Kita
Arabica Gayo 3
null
document.querySelector(css)mengembalikan elemen pertama yang cocok, ataunull.document.querySelectorAll(css)mengembalikan semua kecocokan sebagai NodeList (loop denganfor...of, atau konversi dengan[...list]).- Keduanya menerima CSS selector apa pun, sama seperti yang kamu tulis di modul-modul CSS.
Hasil null adalah bug DOM klasik: document.querySelector("#tilte").textContent crash dengan "Cannot read properties of null". Cek selector-mu saat melihat error itu.
Mengubah teks, class, dan atribut
Begitu kamu punya sebuah elemen, kamu bisa mengubahnya. Saat sebuah produk habis, labelnya harus berbunyi "Sold out" dan tampil keabu-abuan. Saat kamu menambahkan sesuatu ke keranjang, penghitung kecil di pojok harus naik. Masing-masing adalah perubahan kecil pada teks, class, atau atribut sebuah elemen:
document.body.innerHTML = `<p class="stock">In stock</p><a class="link" href="/old">Details</a><span id="cart-count">0</span>`;
const stock = document.querySelector(".stock");
stock.textContent = "Sold out";
stock.classList.add("sold-out");
stock.classList.remove("stock");
console.log(stock.className, stock.classList.contains("sold-out"));
const link = document.querySelector(".link");
link.setAttribute("href", "/product/2");
console.log(link.getAttribute("href"));
const counter = document.querySelector("#cart-count");
counter.textContent = Number(counter.textContent) + 1;
console.log(counter.textContent);
sold-out true
/product/2
1
textContentmembaca atau mengganti teks sebuah elemen.classList.add,.remove,.toggle,.containsmengelola class. Utamakan mengubah class (dan biarkan CSS yang menentukan tampilannya) daripada mengaturelement.style.colorsecara langsung: gaya tetap di CSS.getAttribute/setAttributeuntuk atribut sepertihref,src,disabled.- Teks dari DOM selalu berupa string: konversi dengan
Number(...)sebelum berhitung.
Atribut data
Atribut data-* menyimpan informasimu sendiri di elemen, yang bisa dibaca lewat element.dataset:
document.body.innerHTML = `<li class="product" data-id="7" data-price="95000">Arabica Gayo</li>`;
const item = document.querySelector(".product");
console.log(item.dataset.id, item.dataset.price);
item.dataset.inCart = "true";
console.log(item.outerHTML);
7 95000
<li class="product" data-id="7" data-price="95000" data-in-cart="true">Arabica Gayo</li>
data-in-cart di HTML menjadi dataset.inCart di JavaScript (tanda strip menjadi camelCase).
Membuat, menambahkan, dan menghapus elemen
Kadang elemennya belum ada. Saat pelanggan menambahkan produk, daftar keranjang butuh baris baru untuk masing-masing, dan saat mereka membuang sesuatu, barisnya harus hilang. Kamu membangun elemen dari data lalu menambahkannya ke halaman:
document.body.innerHTML = `<ul id="cart"></ul>`;
const list = document.querySelector("#cart");
const items = [{ name: "Arabica Gayo", qty: 2 }, { name: "French Press", qty: 1 }];
for (const item of items) {
const li = document.createElement("li");
li.textContent = item.name + " × " + item.qty;
li.classList.add("cart-item");
list.append(li);
}
console.log(list.children.length, list.lastElementChild.textContent);
list.firstElementChild.remove();
console.log(list.children.length);
2 French Press × 1
1
createElement membuat elemen baru, append menambahkannya di akhir sebuah induk (prepend untuk di awal), dan remove() mengeluarkan elemen dari halaman.
innerHTML: praktis dan berbahaya
element.innerHTML = "..." mengganti isi sebuah elemen dengan HTML yang di-parse dari string. Ini praktis untuk markup tetap milikmu sendiri, seperti di contoh-contoh di atas. Jangan pernah memakainya dengan data dari pengguna atau API:
document.body.innerHTML = `<div id="review"></div>`;
const review = document.querySelector("#review");
const userText = "<img src=x onerror=alert('hacked')>";
review.textContent = userText; // safe: shown as text
console.log(review.innerHTML);
<img src=x onerror=alert('hacked')>
Dengan innerHTML = userText, browser akan membuat gambar itu dan menjalankan kode penyerang: serangan cross-site scripting (XSS). Dengan textContent, string yang sama ditampilkan sebagai teks yang tidak berbahaya. Bangun elemen dengan createElement + textContent saat ada bagian konten yang berasal dari luar kodemu.
querySelector/querySelectorAll untuk menemukan elemen (null berarti tidak ada yang cocok); textContent, classList dan setAttribute/dataset untuk mengubahnya; createElement + append untuk menambah, remove() untuk menghapus. Gunakan textContent, jangan pernah innerHTML, untuk data yang bukan milikmu.
Mengapa ini penting untuk kode yang bersih
Jaga kode DOM tetap tipis: hitung data di function biasa, lalu biarkan satu function kecil yang me-render data itu ke halaman. Toggle class alih-alih mengatur inline style, supaya tampilan tetap di CSS. Dan pilih elemen sekali, di dekat bagian atas, dengan nama yang jelas (const cartList = ...), alih-alih mengulang selector di seluruh kode.
Rangkuman
document.querySelector(sel)→ kecocokan pertama ataunull;querySelectorAll(sel)→ semua kecocokan.el.textContent,el.classList.add/remove/toggle/contains,el.getAttribute/setAttribute,el.dataset.document.createElement(tag),parent.append(child),el.remove().- Teks DOM berupa string: konversi sebelum berhitung.
innerHTMLhanya untuk markup tepercaya;textContentuntuk apa pun dari pengguna atau API.