Cara Browser Membaca HTML

Proses parsing HTML menjadi pohon DOM dan alasan markup yang cacat tetap dirender.

Daftar isi

Browser tidak menampilkan berkas HTML apa adanya. Berkasnya dibaca dari byte pertama hingga byte terakhir, diurai sesuai algoritma yang ditetapkan spesifikasi, lalu disusun menjadi pohon DOM yang siap diperiksa dan diubah. Topik ini menjelaskan alur pembacaan tersebut dari awal hingga akhir, beserta alasan markup yang cacat tetap dirender dan bukan ditolak. Pemahaman ini menjadi dasar untuk membaca pohon DOM melalui DevTools, yaitu panel alat pengembang bawaan browser.

Dari byte ke pohon DOM#

Proses membaca dokumen HTML berjalan melalui beberapa tahap yang berurutan. Browser menerima deretan byte, yaitu satuan data mentah, dari server atau dari berkas lokal, lalu mengonversinya menjadi karakter sesuai pengodean yang tercantum, misalnya UTF-8. Karakter-karakter itu kemudian dikelompokkan menjadi token, yakni potongan bermakna seperti tag pembuka, tag penutup, atribut, dan teks. Tahap terakhir adalah pembentukan pohon, yaitu penyusunan token menjadi simpul yang saling berhubungan. Seluruh proses ini disebut parsing, dan komponen browser yang mengerjakannya disebut parser.

Hasil akhirnya berupa pohon DOM (Document Object Model), yakni representasi dokumen berupa objek yang saling bertumpuk. Setiap elemen menjadi simpul, setiap teks juga menjadi simpul, dan hubungan induk-anak mengikuti sarang elemen pada dokumen. Diagram berikut memperlihatkan dokumen kecil beserta pohon DOM-nya, termasuk simpul teks yang sering terlepas dari perhatian.

Dokumen HTML dan pohon DOM hasil penguraiannyaindex.html<!doctype html><html><head><title>Jadwal Piket</title></head><body><h1>Jadwal Piket</h1><p>Senin: Ali</p></body></html>parsingPohon DOMhtmlheadbodytitleh1pJadwal PiketJadwal PiketSenin: AliKotak bergaris putus-putus adalah simpul teks (#text)
Dokumen HTML dan pohon DOM hasil penguraiannya

Pohon pada diagram menegaskan dua hal. Pertama, teks bukan hiasan pada tag, melainkan simpul tersendiri yang menjadi anak elemen pembungkusnya. Kedua, pohon DOM berisi simpul hasil penafsiran, sehingga isinya tidak selalu sama dengan teks sumber apa adanya. Perbedaan ini paling terasa ketika markupnya cacat, sebagaimana dibahas pada bagian berikutnya.

Tokenisasi dan pembentukan pohon#

Tokenizer bekerja secara berurutan dari karakter pertama hingga karakter terakhir. Saat menemui tanda kurung siku pembuka, ia membaca nama tag beserta atributnya, lalu mengeluarkan token yang sesuai. Teks di luar tag juga dikeluarkan sebagai token teks. Semua tahap ini berlangsung otomatis dan selesai sangat cepat, bahkan untuk halaman yang panjang.

Token yang dikeluarkan langsung dikonsumsi oleh pembentuk pohon. Untuk setiap token tag pembuka, pembentuk pohon membuat simpul elemen dan menjadikannya anak dari elemen yang sedang terbuka. Token tag penutup menutup simpul yang sesuai, sedangkan token teks menjadi simpul teks pada posisi saat itu. Contoh berikut beserta urutan tokennya memperlihatkan pola tersebut.

html Contoh: dokumen kecil yang diurai
<h1>Jadwal Piket</h1>
<ul>
  <li>Senin: Ali</li>
  <li>Selasa: Budi</li>
</ul>

Urutan token untuk contoh di atas dimulai dari tag pembuka h1, teks Jadwal Piket, tag penutup h1, lalu tag pembuka ul dan seterusnya. Pembentuk pohon meletakkan simpul ul setelah simpul h1, dan setiap li sebagai anak dari ul. Aturan yang menentukan induk yang tepat bukan tebakan, melainkan algoritma lengkap pada spesifikasi parsing WHATWG. Karena semua browser menerapkan algoritma yang sama, dokumen yang sama menghasilkan pohon yang sama di mana pun.

Markup cacat tetap dirender#

Parser HTML dirancang untuk tidak pernah menyerah pada masukan apa pun. Apabila ada tag yang tidak tertutup, tag yang salah sarang, atau elemen yang tidak dikenal, parser meneruskan pekerjaannya dengan menerapkan aturan pemulihan error (error recovery) yang dirinci pada spesifikasi. Konsekuensinya, halaman dengan markup cacat tetap dirender, dan tidak ada halaman error sebagaimana parser XML yang berhenti pada kesalahan pertama.

Pemulihan yang paling umum adalah penutupan otomatis. Contoh berikut memuat dua paragraf tanpa satu pun tag penutup p.

html Contoh: dua paragraf tanpa tag penutup p
<p>Paragraf pertama
<p>Paragraf kedua

Saat token tag pembuka p kedua tiba, pembentuk pohon menutup paragraf sebelumnya secara otomatis, sebab paragraf tidak boleh bersarang. Di akhir dokumen, paragraf yang masih terbuka juga ditutup. Aturan serupa berlaku untuk puluhan kasus lain, mulai dari elemen yang muncul di tempat yang tidak seharusnya hingga tag yang saling menyilang. Tag yang saling menyilang, misalnya b yang ditutup ketika i masih terbuka, dipulihkan dengan memindahkan sisa elemen yang tersangkut, sehingga pohon akhirnya tetap sah. Hasil akhirnya selalu pohon yang sah, tetapi pohon itu tidak selalu sama dengan maksud penulisnya.

Cara kerja pemeriksa markup dapat dirasakan tanpa browser. Skrip berikut memeriksa markup yang sama dengan contoh pada bagian tokenisasi, ditambah satu paragraf penutup yang tag penutupnya sengaja dilepas. Skrip mencatat tag pembuka pada sebuah tumpukan, lalu mengosongkannya pada tag penutup, dan sisa tumpukan di akhir penelusuran berarti ada tag yang tidak tertutup. Detail bahasa JavaScript pada skrip ini belum perlu dipahami, karena yang penting adalah keluarannya.

js
// pemeriksa sederhana untuk menemukan tag yang tidak tertutup
const markup = `<h1>Jadwal Piket</h1>
<ul>
  <li>Senin: Ali</li>
  <li>Selasa: Budi</li>
</ul>
<p>Paragraf penutup`;

const temuan = markup.match(/<\/?[a-z][a-z0-9]*[^>]*>/g) || [];
const tumpukan = [];
for (const tag of temuan) {
  const nama = tag.match(/[a-z][a-z0-9]*/)[0];
  if (!tag.startsWith('</')) {
    tumpukan.push(nama);
  } else {
    tumpukan.pop();
  }
}
for (const sisa of tumpukan) {
  console.log(`<${sisa}> tidak pernah ditutup`);
}
console.log(`pemeriksaan selesai, ${temuan.length} tag dipindai`);
text Keluaran
<p> tidak pernah ditutup
pemeriksaan selesai, 9 tag dipindai

Keluaran melaporkan satu tag p yang tidak pernah ditutup, persis seperti laporan validator sungguhan. Browser menangani kasus yang sama dengan diam-diam menutup paragraf tersebut di akhir dokumen, sedangkan pemeriksa menyatakannya terbuka. Perbedaan sikap inilah yang membuat validator tetap diperlukan sekalipun halaman tampak normal.

DOM sebagai jembatan menuju JavaScript#

Pohon DOM bukan sekadar struktur internal, melainkan juga API (Application Programming Interface) yang terbuka bagi JavaScript. Melalui objek document, skrip dapat mencari simpul tertentu, membaca isinya, dan mengubahnya. Perubahan pada simpul langsung tampak pada halaman yang sedang tampil. Inilah jalannya lapisan perilaku bekerja di atas lapisan struktur.

Jembatan ini berjalan dua arah. JavaScript membaca keadaan halaman dari simpul DOM, misalnya isi teks dan nilai atribut, lalu menuliskan keadaan baru dengan mengubah simpul yang sama. Interaksi yang lazim pada web modern, mulai dari menu yang membuka dan menutup hingga daftar yang diperbarui tanpa memuat ulang, pada dasarnya adalah perubahan pada simpul DOM.

js
// tidak-dijalankan
// kode ini memerlukan browser dan tidak dapat dieksekusi dengan node
const judul = document.querySelector('h1');
console.log(judul.textContent);
judul.textContent = 'Jadwal Piket Akhir Pekan';

Skrip di atas sengaja tidak dieksekusi di sini karena memerlukan browser, bukan runtime JavaScript biasa. Baris pertama mencari simpul h1 pertama pada pohon, baris kedua menampilkan teksnya ke console, dan baris ketiga mengganti teks itu. Setelah baris ketiga berjalan, halaman yang tampil ikut berubah, sedangkan berkas HTML tidak tersentuh. Ketidaksamaan ini menunjukkan bahwa DOM adalah salinan hidup dari berkas, bukan berkas itu sendiri.

Mengamati pohon DOM lewat DevTools#

Pohon DOM pada halaman mana pun dapat dilihat langsung melalui DevTools. Buka panelnya dengan menekan F12 atau Ctrl+Shift+I pada Windows dan Linux, atau Cmd+Option+I pada macOS, lalu pindah ke tab Elements. Tab Elements menampilkan pohon lengkap beserta simpul teksnya, dan elemen yang disorot pada panel ikut disorot pada halaman. Pemeriksaan semacam ini memudahkan pengenalan struktur tanpa membaca seluruh berkas, dan tidak berisiko karena sifatnya hanya melihat.

Percobaan kecil memperjelas perbedaan tersebut. Buat berkas berisi paragraf tanpa tag penutup, buka melalui browser, lalu bandingkan tab Elements dengan tampilan view source. Teks sumber memperlihatkan tag p yang masih terbuka, sedangkan tab Elements menampilkan paragraf yang sudah ditutup rapi. Suntingan teks pada tab Elements juga hanya mengubah salinan di memori, dan muat ulang halaman mengembalikan semuanya pada kondisi semula.

Kesalahan umum#

  • Menganggap markup sah karena dirender tanpa keluhan. Browser memulihkan hampir semua kesalahan secara diam-diam, sehingga halaman yang tampak normal dapat berisi markup cacat. Biasakan memeriksa dokumen melalui validator sebelum dokumen dinyatakan selesai, sekalipun tampilannya sudah tampak benar.
  • Menyamakan perilaku parser HTML dengan parser XML. XML berhenti pada kesalahan pertama dan menampilkan pesan error, sedangkan HTML melanjutkan pekerjaan dengan pemulihan. Ekspektasi pesan error dari browser karenanya dilepas, dan pemeriksaan dipindahkan ke alat khusus.
  • Mengira suntingan pada tab Elements tersimpan permanen. Panel itu memperlihatkan salinan hidup di memori, bukan berkas sumbernya. Muat ulang halaman mengembalikan semuanya, sehingga perubahan permanen tetap harus ditulis pada berkas sumber.
  • Mencari teks sumber di dalam tab Elements. Panel tersebut memperlihatkan DOM yang sudah diperbaiki, sehingga tag yang cacat tidak tampak di sana dan suntingan memori pun ikut tercatat. Teks asli yang dikirim server hanya terlihat melalui view source. Perbedaan antara keduanya justru berguna, karena menunjukkan pekerjaan pemulihan yang dilakukan parser.

Ringkasan#

  • Browser mengurai HTML melalui tahap byte, karakter, token, dan simpul hingga menjadi pohon DOM.
  • Elemen dan teks masing-masing menjadi simpul, dan susunannya ditentukan algoritma parsing pada spesifikasi.
  • Parser HTML memulihkan diri dari markup cacat tanpa pesan error, sehingga halaman tetap dirender dan kesalahannya tidak terlihat.
  • Karena pemulihan itu diam-diam, keabsahan markup diperiksa dengan validator, sedangkan pohon DOM diperiksa melalui tab Elements DevTools.

Referensi#