Chapter sebelumnya berhenti tepat saat node-node selesai dirangkai menjadi DOM. Kini kita berhenti lebih lama pada hasil tersebut: apa itu DOM, bagaimana bentuknya, dan mengapa DOM tidak selalu sama dengan markup yang kita tulis. Pemahaman ini penting karena DevTools, JavaScript, dan sebagian besar perilaku browser bekerja pada DOM — bukan pada file HTML di server.

DOM adalah Representasi Objek Dokumen #

DOM (Document Object Model) adalah representasi berbasis objek dari sebuah dokumen di dalam memori browser. File HTML hanyalah teks; DOM adalah hasil pembacaan teks tersebut. Setiap element menjadi objek yang memiliki properti dan method, siap dibaca maupun diubah oleh program. Objek document menjadi titik masuknya — melalui objek inilah JavaScript menjangkau bagian mana pun dari halaman.

File di server tidak berubah ketika DOM berubah. Perubahan hanya terjadi pada salinan di memori browser, dan salinan itu dibangun ulang dari nol setiap kali halaman dimuat ulang.

Struktur Pohon: Element, Teks, dan Atribut #

DOM berbentuk pohon. Node paling atas adalah document, di bawahnya element html, lalu head dan body, dan seterusnya mengikuti nested markup. Tiga jenis node yang paling sering kita temui:

  • Element node: satu objek untuk setiap element, misalnya h1, p, dan li.
  • Text node: isi teks di dalam element; spasi dan baris baru pada markup pun menjadi bagian teks.
  • Atribut: nilai seperti href dan class tersimpan pada objek element yang memilikinya, bukan sebagai node anak dalam pohon.

File berikut adalah halaman kecil yang akan kita bedah wujud DOM-nya. Simpan sebagai contoh-dom-overview.html:

<!doctype html>
<html lang="id">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Daftar Belanja Budi</title>
</head>
<body>
  <h1>Daftar Belanja</h1>
  <p>Budi menulis daftar sebelum pergi ke pasar.</p>
  <ul>
    <li>Beras 5 kg</li>
    <li>Gula 1 kg</li>
  </ul>
</body>
</html>

Setelah diparsing, wujud pohon DOM-nya kira-kira sebagai berikut:

text
document
└── html
    ├── head
    │   ├── meta (charset)
    │   ├── meta (viewport)
    │   └── title → teks "Daftar Belanja Budi"
    └── body
        ├── h1 → teks "Daftar Belanja"
        ├── p → teks "Budi menulis daftar sebelum pergi ke pasar."
        └── ul
            ├── li → teks "Beras 5 kg"
            └── li → teks "Gula 1 kg"

Berikut peran setiap bagian file dalam pohon di atas:

  1. Baris 4 (<meta charset="utf-8">): element node sederhana tanpa isi; atribut charset tersimpan pada objek element-nya.
  2. Baris 6 (<title>): element node dengan satu text node sebagai anak; isi teks inilah yang tampil pada tab browser.
  3. Baris 9-10 (<h1> dan <p>): dua element node sejajar di bawah body, masing-masing membawa satu text node.
  4. Baris 11-14 (<ul> dan dua <li>): contoh nested; kedua element li menjadi anak element ul, dan parser memastikan penempatannya tepat.
  5. Baris 15-16 (tag penutup): parser menutup node yang tersisa, lalu pohon dianggap selesai dan siap diperlakukan CSS.

DOM Bersifat Live #

DOM disebut live: perubahan lewat JavaScript langsung terlihat pada halaman tanpa reload. Buka contoh di atas, lalu jalankan baris berikut pada console DevTools:

javascript
document.querySelector("h1").textContent = "Daftar Belanja Dewi";

Heading di layar berganti nama seketika, dan panel Elements ikut menampilkan teks baru. Yang tidak berubah adalah file contoh-dom-overview.html di server — hanya salinan memori sajalah yang berubah. Sifat live inilah yang memungkinkan JavaScript memperbarui sebagian halaman tanpa reload.

Ketika DOM Berbeda dari Markup #

Parser HTML tidak berhenti ketika menemui markup yang salah tulis; parser memperbaikinya mengikuti aturan error recovery yang dibakukan pada spesifikasi. Akibatnya, DOM bisa berbeda dari yang tampak kita tulis. Contoh pertama, tag penutup p boleh ditinggalkan ketika paragraf lain menyusul:

html Markup tanpa tag penutup p
<p>Butir pertama catatan Budi
<p>Butir kedua catatan Budi

Markup tersebut tampak seperti satu paragraf berisi paragraf lain. Parser membacanya secara berbeda: ketika token start tag p kedua tiba, parser menutup paragraf pertama secara implisit. DOM yang terbentuk berisi dua element p yang bersaudara — sejajar di bawah body, bukan satu node di dalam node lain.

Contoh kedua, tag penutup yang salah pasang:

html Tag penutup yang tidak berpasangan
<p>Teks awal <b>tebal</i> berlanjut di sini</p>

Penulisnya bermaksud menghentikan penekanan pada </i>, padahal yang dibuka adalah b. Parser mengabaikan tag penutup yang tidak berpasangan, sehingga teks “berlanjut di sini” ikut berada di dalam element b. Panel Elements akan menampilkan b yang menampung teks setelahnya — bukan hanya kata “tebal”.

Satu konsekuensi terakhir sering mengejutkan pemula: panel Elements pada DevTools menampilkan DOM live, bukan isi file. Bila parser memperbaiki markup atau JavaScript mengubah halaman, panel Elements menunjukkan hasil akhirnya. Chapter berikutnya membandingkan panel ini dengan view-source, pintu lain menuju halaman yang sama.

Referensi resmi untuk pendalaman:

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

contoh-dom-overview.html

Unduh berkas
html
<!doctype html>
<html lang="id">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Daftar Belanja Budi</title>
</head>
<body>
  <h1>Daftar Belanja</h1>
  <p>Budi menulis daftar sebelum pergi ke pasar.</p>
  <ul>
    <li>Beras 5 kg</li>
    <li>Gula 1 kg</li>
  </ul>
</body>
</html>