Siti sedang menata halaman menu lokakaryanya. Sudah ada satu elemen terpilih — misalnya item menu “Kopi” — dan tugas berikutnya bukan mencari ulang dari document, melainkan bergerak dari titik itu: ke induknya untuk mengubah panel, ke item di sampingnya untuk menghapus tanda aktif, atau ke leluhur tertentu untuk memeriksa konteks. Perpindahan antar node yang sudah berdekatan inilah yang disebut DOM traversal.
Traversal menghemat dua hal: kode tidak perlu menulis selector panjang berulang-ulang, dan browser tidak perlu memindai seluruh dokumen lagi karena tetangga sudah diketahui posisinya. Chapter ini membahas properti perpindahan ke atas, ke bawah, dan ke samping, ditambah method closest() untuk mencari leluhur yang cocok.
Halaman contoh kita sengaja dibuat ringkas agar setiap properti mudah dilacak:
<nav id="menu">
<h2 class="judul">Kategori</h2>
<ul>
<li class="aktif">Kopi</li>
<li>Non-Kopi</li>
<li>Camilan</li>
</ul>
<p class="catatan">Tiga kategori menu hari ini.</p>
</nav>Naik, Turun, dan ke Samping #
Satu berkas mencakup ketiga arah perpindahan. Kita mulai dari elemen ul, berhenti pada item pertama, lalu bergerak dari sana:
// jelajahi-keluarga.js — berpindah antar elemen yang bertetangga
const daftar = document.querySelector("#menu ul");
const kopi = daftar.firstElementChild;
// 1. naik ke induk
console.log(kopi.parentElement.nodeName); // UL
console.log(daftar.parentElement.id); // menu
// 2. turun ke anak
console.log(daftar.children.length); // 3
console.log(kopi.textContent); // Kopi
console.log(daftar.lastElementChild.textContent); // Camilan
// 3. lompat ke samping
console.log(kopi.nextElementSibling.textContent); // Non-Kopi
console.log(kopi.nextElementSibling.nextElementSibling.textContent); // Camilan
console.log(daftar.lastElementChild.previousElementSibling.textContent); // Non-Kopi
// 4. closest: leluhur terdekat yang cocok
const aktif = document.querySelector("#menu li.aktif");
console.log(aktif.closest("ul").nodeName); // UL
console.log(aktif.closest("#menu").id); // menu
console.log(aktif.closest("header")); // null
Output yang Dihasilkan (console browser):
UL
menu
3
Kopi
Camilan
Non-Kopi
Camilan
Non-Kopi
UL
menu
nullKeluaran dicatat dari eksekusi nyata di lingkungan jsdom dan identik dengan console browser. Mari kita bedah:
- Baris 4–5 (pemegang awal):
firstElementChilddipilih — bukanfirstChild— agar kita langsung mendapat elemenlipertama tanpa tersandung node teks spasi (perbedaannya dibedah di chapter Memahami DOM). - Baris 7–8 (
parentElement):kopi.parentElementadalahUL— tepat satu tingkat di atasnya — dandaftar.parentElementadalah elemenNAVyang bernilaiidmenu. Satu properti, satu lantai naik. - Baris 11–13 (
children,textContent,lastElementChild):daftar.children.lengthbernilai3karena propertichildrenhanya berisi node elemen.lastElementChildmenunjuk item terakhir,Camilan, tanpa perlu tahu indeksnya. - Baris 16–18 (
nextElementSiblingdanpreviousElementSibling): dariKopi, dua kali lompatan ke samping menghasilkanNon-KopilaluCamilan. Arah sebaliknya bekerja persis sama — dariCamilanmundur satu langkah keNon-Kopi. Rantai semacam ini nyaman untuk jarak pendek; untuk jarak panjang, gabungkan denganclosest()atau selector. - Baris 22–24 (
closest()): method ini dicari mulai dari elemen itu sendiri, lalu naik ke leluhur satu per satu hingga menemukan elemen yang cocok dengan selector.closest("ul")langsung kena karena itemlimemang berada di dalamul;closest("header")tidak menemukan apa pun sehingga mengembalikannull.
Akses Berindeks dan Penghitung #
Selain tiga properti arah, dua cara akses lain sering berguna ketika posisi elemen diketahui sebagai urutan, bukan sebagai relasi:
const daftar = document.querySelector("#menu ul");
console.log(daftar.childElementCount); // 3
console.log(daftar.children[2].textContent); // Camilan
Koleksi children berupa HTMLCollection yang bisa diakses berindeks seperti array — children[0] anak pertama, children[2] anak ketiga — meski bukan array sungguhan (tidak punya map atau forEach; bungkus dengan Array.from bila butuh method array). Properti childElementCount adalah kependekan dari children.length: sama persis hasilnya, hanya lebih langsung menyatakan niat “berapa jumlah anak”.
Traversal atau Selector? #
Setelah melihat dua alat, pertanyaan wajar muncul: kapan bergerak dari elemen yang sudah dipegang, kapan mencari ulang? Tiga pertimbangan membantu memilih:
- Kekuatan pencarian. Selector jauh lebih luwes untuk ciri yang rumit —
closestpun hanya menerima ciri, sedangkanquerySelectorbisa menyaring atribut, urutan, dan sarang sekaligus. - Stabilitas pegangan. Hasil traversal adalah elemen yang sama yang sedang dipegang, sehingga perubahan struktur di sekitarnya tidak memengaruhi langkah berikutnya.
- Keterbacaan maksud.
kopi.parentElementmenyatakan “induk dari kopi” — sebuah hubungan yang pasti — sementara selector menyatakan “cari dari nol”. Untuk relasi yang tetap, traversal sering lebih pendek dan lebih jujur pada maksud.
Dalam praktiknya keduanya berdampingan: selector untuk titik masuk pertama, traversal untuk langkah-langkah berikutnya di sekitar titik itu.
Cara Kerja closest() #
Perlu digarisbawahi bahwa closest() ikut menguji elemen awal sebelum naik. Jika aktif sendiri cocok dengan selector, aktif itulah hasilnya. Alurnya:
mulai dari elemen → cocok dengan selector?
→ ya: kembalikan elemen ini
→ tidak: naik ke parentElement, ulangi pemeriksaan
→ sampai root tanpa kecocokan: kembalikan nullKarena menerima selector CSS penuh, closest("#menu"), closest(".panel"), bahkan closest("a.tombol") semuanya valid. Sifat ini menjadikan closest() alat favorit saat halaman punya struktur bersarang yang dalam — kita cukup memegang elemen terdalam dan “bertanya ke atas” tentang konteksnya.
Satu lagi yang penting: hasil closest() bukan salinan, melainkan referensi ke elemen yang sama persis dengan yang ada di pohon. Perbandingan identitas membuktikannya:
const kopi = document.querySelector("#menu li.aktif");
console.log(kopi.closest("nav") === document.querySelector("#menu")); // true
Konsekuensinya praktis: setiap perubahan yang dilakukan pada hasil closest() — mengganti class, atribut, isi — langsung terlihat pada elemen asli di dokumen, karena memang hanya ada satu elemen itu.
Peta Properti Penjelajah #
Tabel berikut merangkum properti traversal yang dipakai di berkas contoh, ditambah beberapa saudaranya:
| Properti | Arah | Mengembalikan |
|---|---|---|
parentElement | naik satu tingkat | elemen induk, atau null |
children | turun (koleksi) | HTMLCollection anak yang berupa elemen |
firstElementChild | turun | anak elemen pertama, atau null |
lastElementChild | turun | anak elemen terakhir, atau null |
nextElementSibling | samping | elemen berikutnya, atau null |
previousElementSibling | samping | elemen sebelumnya, atau null |
Semua properti di atas hanya bergerak antar elemen. Versi tanpa akhiran Element (parentNode, childNodes, firstChild, nextSibling) juga ada, tetapi ikut menyeret node teks dan komentar — gunakan hanya bila memang membaca level node mentah.
Dengan kemampuan memilih dan menjelajah, kita sudah bisa berpindah ke titik mana pun di pohon. Chapter berikutnya mengisi titik-titik itu dengan konten: membaca serta mengganti teks dan markup.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
jelajahi-keluarga.js
Unduh berkas// jelajahi-keluarga.js — berpindah antar elemen yang bertetangga
const daftar = document.querySelector("#menu ul");
const kopi = daftar.firstElementChild;
// 1. naik ke induk
console.log(kopi.parentElement.nodeName); // UL
console.log(daftar.parentElement.id); // menu
// 2. turun ke anak
console.log(daftar.children.length); // 3
console.log(kopi.textContent); // Kopi
console.log(daftar.lastElementChild.textContent); // Camilan
// 3. lompat ke samping
console.log(kopi.nextElementSibling.textContent); // Non-Kopi
console.log(kopi.nextElementSibling.nextElementSibling.textContent); // Camilan
console.log(daftar.lastElementChild.previousElementSibling.textContent); // Non-Kopi
// 4. closest: leluhur terdekat yang cocok
const aktif = document.querySelector("#menu li.aktif");
console.log(aktif.closest("ul").nodeName); // UL
console.log(aktif.closest("#menu").id); // menu
console.log(aktif.closest("header")); // null