Halaman toko Budi berisi puluhan elemen: judul, daftar produk, panel stok, tombol keranjang. Sebelum JavaScript bisa mengubah salah satunya — mengganti teks judul, menandai produk unggulan, memperbarui daftar — kode harus memegang elemen yang tepat terlebih dahulu. Memilih elemen (element selection) adalah langkah pertama dari hampir setiap pekerjaan DOM, dan browser menyediakan beberapa method dengan karakter yang berbeda.
Chapter ini menuntaskan tiga cara utama — getElementById, querySelector, dan querySelectorAll — membedakan koleksi live dan static, serta menangani kasus ketika pencarian tidak menemukan apa pun. Halaman contohnya bisa diunduh sebagai halaman-toko.txt.
Tiga Cara Memilih Elemen #
<!DOCTYPE html>
<html>
<body>
<h1 id="judul">Toko Kopi Budi</h1>
<ul class="produk-list">
<li class="produk">Kopi Gayo</li>
<li class="produk unggulan">Teh Melati</li>
<li class="produk">Gula Aren</li>
</ul>
<p class="info">Stok diperbarui setiap pagi.</p>
<script src="cari-elemen.js"></script>
</body>
</html>Berkas berikut memakai ketiga method di atas pada halaman yang sama. Perhatikan bahwa setiap cara punya bentuk argumen berbeda: getElementById menerima nama id tanpa tanda pagar, sedangkan kedua method querySelector menerima selector CSS utuh.
// cari-elemen.js — tiga cara memilih elemen dari halaman toko
// 1. getElementById: satu id, satu elemen
const judul = document.getElementById("judul");
console.log(judul.textContent);
// 2. querySelector: selector CSS, kecocokan pertama
const unggulan = document.querySelector(".produk.unggulan");
console.log(unggulan.textContent);
const terakhir = document.querySelector(".produk-list li:last-child");
console.log(terakhir.textContent);
// 3. querySelectorAll: semua kecocokan (static NodeList)
const produk = document.querySelectorAll(".produk");
console.log(produk.length);
produk.forEach((item, urutan) => {
console.log(`${urutan + 1}. ${item.textContent}`);
});
// 4. ketika tidak ada yang cocok
const promo = document.querySelector("#promo");
console.log(promo);
console.log(promo?.textContent);Output yang Dihasilkan (console browser):
Toko Kopi Budi
Teh Melati
Gula Aren
3
1. Kopi Gayo
2. Teh Melati
3. Gula Aren
null
undefinedKeluaran di atas dicatat dari eksekusi nyata berkas ini pada lingkungan DOM jsdom; console browser menghasilkan baris-baris yang sama. Mari kita bedah:
- Baris 4 (
document.getElementById("judul")): method ini mencari satu elemen yang atributid-nya persis"judul"dan mengembalikan elemen tersebut. Id dijamin unik per dokumen, sehingga pencariannya langsung dan hasilnya paling dapat diprediksi. - Baris 8 (
.produk.unggulan):querySelectormenerima selector CSS apa pun — di sini “elemen yang punya classprodukDANunggulansekaligus”. Dari semua kecocokan, hanya yang pertama dalam urutan dokumen yang dikembalikan, yaituTeh Melati. - Baris 10 (
.produk-list li:last-child): selector bisa bersarang dengan kombinator dan pseudo-class CSS — “elemenliterakhir di dalam.produk-list”. Kemampuan inilah yang membuatquerySelectorsangat fleksibel dibanding method pencarian sederhana. - Baris 14–17 (
querySelectorAlldanforEach): method ini mengembalikan NodeList berisi seluruh kecocokan. NodeList punya methodforEach, sehingga daftar bisa dijajal tanpa konversi apa pun; argumen kedua callback memberi nomor urut otomatis. - Baris 21–23 (
#promoyang tidak ada):querySelectormengembalikannullketika tidak menemukan apa pun. Barispromo?.textContent— optional chaining dari section Operators — mengembalikanundefinedalih-alih melempar galat, sehingga program tetap berlanjut.
Mencari di Dalam Subtree #
Method querySelector dan querySelectorAll tidak hanya milik document — setiap elemen punya keduanya. Ketika dipanggil dari sebuah elemen, pencarian dibatasi pada subtree milik elemen tersebut: hanya keturunannya yang diperiksa, dan pencarian tidak pernah naik ke atas.
const daftar = document.querySelector(".produk-list");
console.log(daftar.querySelectorAll("li").length); // 3
console.log(daftar.querySelector("#judul")); // null — di luar subtree
Batasan ini berguna dalam dua situasi. Pertama, presisi: panel.querySelector("button") hanya melihat tombol di dalam panel, meski halaman penuh tombol lain. Kedua, komponen: dua bagian halaman boleh sama-sama punya elemen ber-id atau ber-class serupa, dan masing-masing kode tetap menemukan punyanya karena pencarian dibatasi dari akar yang berbeda.
Sebaliknya, document.querySelector(...) mengarungi seluruh dokumen karena akarnya adalah dokumen itu sendiri. Kebiasaan yang sehat: mulai dari document untuk titik masuk pertama, lalu turunkan lingkup pencarian mengikuti struktur halaman yang sedang dikerjakan.
Live vs Static: Koleksi yang Berubah Sendiri #
querySelectorAll mengembalikan koleksi static: daftar yang dicuplik (snapshot) pada saat pemanggilan, lalu tidak berubah lagi meski dokumen berubah. Ada keluarga method lain — getElementsByTagName dan getElementsByClassName — yang mengembalikan HTMLCollection live: koleksi yang selalu sinkron dengan kondisi dokumen terkini.
| Method | Koleksi | Sifat | Isi |
|---|---|---|---|
querySelectorAll() | NodeList | static — cuplikan saat dipanggil | hanya elemen |
getElementsByTagName() | HTMLCollection | live — mengikuti dokumen | hanya elemen |
getElementsByClassName() | HTMLCollection | live — mengikuti dokumen | hanya elemen |
Perbedaannya terlihat ketika dokumen berubah setelah koleksi dibuat. Misalkan satu li.produk baru ditambahkan ke daftar toko setelah dua koleksi berikut dibuat:
const statis = document.querySelectorAll(".produk");
const hidup = document.getElementsByClassName("produk");
// (satu li.produk baru ditambahkan ke dokumen di sini)
console.log(statis.length); // 3 — snapshot saat dipanggil, tidak bergeser
console.log(hidup.length); // 4 — koleksi live ikut memperbarui diri
Method penambah elemen pada potongan di atas belum kita pelajari, sehingga snippet ini sengaja kita jalankan penuh dan buktikan keluarannya di chapter Creating Elements. Untuk saat ini, cukup pegang aturannya: koleksi static aman dari perubahan dokumen, koleksi live selalu mencerminkan kondisi terbaru.
Kapan koleksi live justru diinginkan? Ketika kita ingin penghitungan yang selalu segar tanpa memanggil ulang querySelectorAll setiap saat — misalnya getElementsByClassName("produk") yang panjangnya bisa dibaca kapan saja dan selalu benar. Namun kejutan live juga punya sisi buruk: koleksi yang bergeser sendiri di tengah perulangan pernah menghasilkan bug klasik saat menghapus item, dan kisah itu kita bedah di chapter Removing Elements. Sampai saat itu, pilih static sebagai kebiasaan bawaan.
Ketika Tidak Ada yang Cocok #
Kegagalan pencarian punya dua wajah berbeda, dan keduanya perlu dibedakan:
querySelectordangetElementByIdmengembalikannull— nilai yang harus diperiksa sebelum dipakai, karena memanggil properti apa pun padanullmenghasilkanTypeErrordan menghentikan sisa script.querySelectorAll(juga kedua methodgetElementsBy*) mengembalikan koleksi kosong berukuran0— bukannull. PemanggilanforEachpada koleksi kosong berjalan tanpa galat; loop-nya hanya tidak pernah dieksekusi.
console.log(document.querySelectorAll("#promo").length); // 0
console.log(document.getElementById("promo")); // null
Pola penjagaan yang nyaman: bila elemen wajib ada, izinkan TypeError muncul agar kesalahan struktur terlihat saat pengembangan; bila elemen mungkin sengaja tidak ada, gunakan optional chaining (elemen?.teks) atau periksa keberadaannya dengan if terlebih dahulu.
Perhatikan juga beda bentuk kegagalan ketika niat kita “baca jika ada”: querySelectorAll("#promo").length mengembalikan angka yang bisa langsung dibandingkan (0 berarti kosong), sedangkan querySelector("#promo") menuntut pemeriksaan null sebelum dipakai. Memilih method berarti memilih bentuk pemeriksaannya.
Dengan elemen sudah terpilih, pertanyaan berikutnya adalah bergerak dari satu elemen ke tetangganya tanpa mencari ulang dari document — itulah bahasan chapter Traversing DOM.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
cari-elemen.js
Unduh berkas// cari-elemen.js — tiga cara memilih elemen dari halaman toko
// 1. getElementById: satu id, satu elemen
const judul = document.getElementById("judul");
console.log(judul.textContent);
// 2. querySelector: selector CSS, kecocokan pertama
const unggulan = document.querySelector(".produk.unggulan");
console.log(unggulan.textContent);
const terakhir = document.querySelector(".produk-list li:last-child");
console.log(terakhir.textContent);
// 3. querySelectorAll: semua kecocokan (static NodeList)
const produk = document.querySelectorAll(".produk");
console.log(produk.length);
produk.forEach((item, urutan) => {
console.log(`${urutan + 1}. ${item.textContent}`);
});
// 4. ketika tidak ada yang cocok
const promo = document.querySelector("#promo");
console.log(promo);
console.log(promo?.textContent);