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:

html Struktur menu yang dijelajahi
<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):

text
UL
menu
3
Kopi
Camilan
Non-Kopi
Camilan
Non-Kopi
UL
menu
null

Keluaran dicatat dari eksekusi nyata di lingkungan jsdom dan identik dengan console browser. Mari kita bedah:

  1. Baris 4–5 (pemegang awal): firstElementChild dipilih — bukan firstChild — agar kita langsung mendapat elemen li pertama tanpa tersandung node teks spasi (perbedaannya dibedah di chapter Memahami DOM).
  2. Baris 7–8 (parentElement): kopi.parentElement adalah UL — tepat satu tingkat di atasnya — dan daftar.parentElement adalah elemen NAV yang bernilai id menu. Satu properti, satu lantai naik.
  3. Baris 11–13 (children, textContent, lastElementChild): daftar.children.length bernilai 3 karena properti children hanya berisi node elemen. lastElementChild menunjuk item terakhir, Camilan, tanpa perlu tahu indeksnya.
  4. Baris 16–18 (nextElementSibling dan previousElementSibling): dari Kopi, dua kali lompatan ke samping menghasilkan Non-Kopi lalu Camilan. Arah sebaliknya bekerja persis sama — dari Camilan mundur satu langkah ke Non-Kopi. Rantai semacam ini nyaman untuk jarak pendek; untuk jarak panjang, gabungkan dengan closest() atau selector.
  5. 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 item li memang berada di dalam ul; closest("header") tidak menemukan apa pun sehingga mengembalikan null.

Akses Berindeks dan Penghitung #

Selain tiga properti arah, dua cara akses lain sering berguna ketika posisi elemen diketahui sebagai urutan, bukan sebagai relasi:

js Akses berindeks dan penghitung anak
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 — closest pun hanya menerima ciri, sedangkan querySelector bisa 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.parentElement menyatakan “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:

text
mulai dari elemen → cocok dengan selector?
  → ya: kembalikan elemen ini
  → tidak: naik ke parentElement, ulangi pemeriksaan
  → sampai root tanpa kecocokan: kembalikan null

Karena 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:

js closest() mengembalikan elemen yang sama, bukan salinan
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:

PropertiArahMengembalikan
parentElementnaik satu tingkatelemen induk, atau null
childrenturun (koleksi)HTMLCollection anak yang berupa elemen
firstElementChildturunanak elemen pertama, atau null
lastElementChildturunanak elemen terakhir, atau null
nextElementSiblingsampingelemen berikutnya, atau null
previousElementSiblingsampingelemen 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
javascript
// 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