Pada halaman toko kopi Budi ada judul, daftar produk, dan panel stok. Ketika berkas HTML dibuka di browser, yang dilihat pengunjung adalah tampilan; yang dilihat browser adalah struktur. Sebelum satu baris JavaScript pun dijalankan, browser sudah menerjemahkan seluruh dokumen HTML menjadi struktur data yang bisa dibaca program: pohon node yang disebut Document Object Model (DOM).
Tanpa memahami pohon ini, manipulasi halaman terasa seperti menebak-nebak — mengapa sebuah selector menemukan elemen, mengapa teks dan elemen diperlakukan berbeda, dan dari mana object document berasal. Chapter ini membedah fondasi tersebut: bentuk pohon, peran document sebagai pintu masuk, serta perbedaan node vs element yang menjadi bahasa dasar seluruh chapter DOM berikutnya.
Dari HTML ke Pohon Node #
Ketika browser membaca dokumen HTML, parser browser bekerja baris demi baris dari atas ke bawah: setiap tag menjadi sebuah node, setiap potongan teks menjadi node teks, dan setiap komentar menjadi node komentar. Node dibuat segera setelah tag atau teksnya selesai dibaca — sebab itulah script yang diletakkan di tengah dokumen sudah bisa menemukan elemen yang ada di atasnya, tetapi belum bisa menemukan elemen di bawahnya. Node-node tersebut kemudian disusun sesuai sarangnya — tag di dalam tag menjadi anak dari tag di luarnya. Hasil akhirnya adalah satu pohon yang berakar di dokumen.
Perhatikan halaman berikut; berkas lengkapnya bisa diunduh sebagai halaman-profil.txt:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Profil Budi</title>
</head>
<body>
<main id="konten">
<h1>Profil Budi</h1>
<p>Pemilik toko kopi.</p>
</main>
<script src="jelajahi-pohon.js"></script>
</body>
</html>Di dalam memori browser, dokumen di atas tersimpan sebagai pohon berikut:
document (nodeType 9)
└── html (documentElement)
├── head
│ └── title → teks "Profil Budi"
└── body
├── main#konten
│ ├── h1 → teks "Profil Budi"
│ └── p → teks "Pemilik toko kopi."
└── script (jelajahi-pohon.js)Setiap kotak pada gambar adalah satu node, dan garis penghubungnya adalah hubungan induk-anak. Pohon inilah yang diperiksa, dibaca, dan diubah oleh JavaScript sepanjang section DOM — bukan teks HTML mentahnya.
Empat jenis node yang paling sering kita temui:
| nodeType | Nama Konstanta | Contoh di halaman profil |
|---|---|---|
| 1 | ELEMENT_NODE | <main>, <h1>, <p> |
| 3 | TEXT_NODE | teks "Profil Budi" di dalam <h1> |
| 8 | COMMENT_NODE | <!-- catatan --> |
| 9 | DOCUMENT_NODE | document itu sendiri |
document: Pintu Masuk ke Pohon #
Kita tidak bisa memanipulasi apa pun sebelum mendapatkan pegangan ke pohon. Pegangan itu adalah object document — node yang tersedia otomatis di setiap script browser tanpa perlu kita buat. Dari document, kita menuruni pohon: document.documentElement menunjuk elemen <html>, sedangkan document.body menunjuk <body>.
Berkas berikut mengintip pohon halaman profil dari tiga arah: akar dokumen, elemen utama, lalu anak-anaknya.
// jelajahi-pohon.js — mengintip struktur pohon DOM halaman profil
// 1. document: pintu masuk ke pohon
console.log(document.nodeType); // 9 — DOCUMENT_NODE
console.log(document.documentElement.nodeName); // HTML
console.log(document.body.nodeName); // BODY
// 2. turun ke elemen utama
const utama = document.querySelector("#konten");
console.log(utama.nodeType, utama.nodeName); // 1 MAIN
// 3. anak-anak: children (elemen saja) vs childNodes (semua node)
console.log(utama.children.length); // 2
console.log(utama.childNodes.length); // 5
// 4. node teks vs node elemen
console.log(utama.firstChild.nodeName); // #text
console.log(utama.firstElementChild.nodeName); // H1
console.log(utama.firstElementChild.firstChild.nodeType); // 3
Output yang Dihasilkan (console browser):
9
HTML
BODY
1 MAIN
2
5
#text
H1
3Keluaran di atas dicatat dari eksekusi nyata berkas ini pada lingkungan DOM jsdom di terminal; console Chrome dan Firefox menghasilkan sembilan baris yang persis sama. Mari kita bedah:
- Baris 4 (
document.nodeType): nilai9menandai node dokumen penuh — konstantaDOCUMENT_NODE.documentbukan elemen, melainkan akar yang membungkus seluruh pohon. - Baris 5–6 (
documentElementdanbody): propertidocumentElementmenunjuk node bertipe elemen dengan namaHTML, danbodymenunjukBODY. Dua pintu turun yang paling sering dipakai. - Baris 9–10 (
querySelector("#konten")danconsole.log(...)): methodquerySelectormengembalikan satu elemen;nodeType-nya1(element) dannodeName-nyaMAIN. Pemilihan elemen dibedah tuntas di chapter Selecting Elements. - Baris 13–14 (
children.lengthdanchildNodes.length):childrenmenghitung hanya node elemen —2(<h1>dan<p>).childNodesmenghitung semua node —5, karena tiga rangkaian spasi antar tag ikut menjadi node teks. - Baris 17–19 (
firstChild,firstElementChild,nodeType):firstChildberhenti pada node teks (nodeNamebernilai#text), sedangkanfirstElementChildmelompatinya dan menunjukH1. Node teks di dalam<h1>bertipe3— konstantaTEXT_NODE.
Node vs Element #
Dari bedah di atas muncul perbedaan yang akan terus kita pakai: node adalah nama umum untuk segala simpul pohon — elemen, teks, komentar, hingga dokumen — sedangkan element adalah jenis node khusus bertanda nodeType 1 yang mewakili satu tag. Baris <h1>Profil Budi</h1> sebenarnya terdiri dari dua node: satu element dan satu node teks di dalamnya.
Perbedaan ini menjelaskan dua pasangan properti yang tampak mirip:
childrenhanya berisi node element;childNodesberisi semua node, termasuk teks dan komentar. Di halaman profil,children.lengthbernilai2sedangkanchildNodes.lengthbernilai5— selisih tiga adalah node teks berisi spasi.firstElementChildmelompati node teks dan langsung menunjuk<h1>;firstChildberhenti pada node teks spasi (#text) karena node itulah yang paling depan.
Akibat praktisnya: properti tanpa akhiran Element (childNodes, firstChild, lastChild, nextSibling) bekerja pada level node mentah, dan hasilnya sering sulit diprediksi karena spasi sumber ikut terhitung. Untuk pekerjaan sehari-hari, pilih varian berakhiran Element — children, firstElementChild, lastElementChild, nextElementSibling — karena daftarnya bebas dari “gangguan” node teks.
Membaca Nama dan Isi Node #
Berkas contoh memperkenalkan dua properti pemeriksa — nodeName dan nodeType — yang punya satu saudara lagi bernama nodeValue. Ketiganya bekerja pada level node mentah, dan isinya bergantung pada tipe node yang memegangnya:
const h1 = utama.firstElementChild;
console.log(h1.nodeName); // H1
console.log(h1.nodeValue); // null — elemen tidak punya "nilai"
console.log(h1.firstChild.nodeName); // #text
console.log(h1.firstChild.nodeValue); // Profil Budi
Output yang Dihasilkan (console browser):
H1
null
#text
Profil BudiUntuk node elemen, nodeName berisi nama tag dengan huruf besar — konvensi dokumen HTML — sedangkan nodeValue selalu null, karena elemen tidak “berisi nilai”; elemen berisi anak. Untuk node teks, nodeName bernilai #text dan nodeValue berisi string yang ia simpan.
Dari sinilah kegunaan textContent terlihat kembali: properti itu mengumpulkan nodeValue dari seluruh node teks di dalam elemen secara otomatis, sehingga untuk membaca isi kita jarang perlu menyentuh nodeValue secara langsung. Level node mentah dibutuhkan terutama saat memeriksa struktur — dan saat menulis kode yang harus bekerja untuk tipe node apa pun.
Dengan pohon dan pintu masuknya di tangan, langkah berikutnya adalah memilih node tertentu secara presisi di antara ratusan node lain — tugas chapter Selecting Elements.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
jelajahi-pohon.js
Unduh berkas// jelajahi-pohon.js — mengintip struktur pohon DOM halaman profil
// 1. document: pintu masuk ke pohon
console.log(document.nodeType); // 9 — DOCUMENT_NODE
console.log(document.documentElement.nodeName); // HTML
console.log(document.body.nodeName); // BODY
// 2. turun ke elemen utama
const utama = document.querySelector("#konten");
console.log(utama.nodeType, utama.nodeName); // 1 MAIN
// 3. anak-anak: children (elemen saja) vs childNodes (semua node)
console.log(utama.children.length); // 2
console.log(utama.childNodes.length); // 5
// 4. node teks vs node elemen
console.log(utama.firstChild.nodeName); // #text
console.log(utama.firstElementChild.nodeName); // H1
console.log(utama.firstElementChild.firstChild.nodeType); // 3