DOM Manipulation Efisien — Performance

DOM Manipulation yang Efisien Manipulasi DOM adalah salah satu operasi paling mahal di browser. Setiap perubahan DOM bisa trigger layout recalculation…

DOM adalah pohon elemen halaman yang dipegang browser, yang bisa dibaca dan diubah JavaScript. Tiap kali ukuran atau isi elemen berubah, browser harus menghitung ulang layout, yaitu posisi dan ukuran semua kotak di halaman, sebelum menggambarnya. Manipulasi DOM yang efisien artinya bikin hitungan ulang itu sesedikit mungkin. Bayangin kamu menata ulang perabot kamar sambil ada yang terus nanya "lebar lorongnya sekarang berapa?". Kalau tiap habis geser satu lemari kamu harus mengukur ulang seluruh kamar, capek. Lebih hemat: ukur semuanya sekali, catat, baru geser semua perabot.

🧪 Tebakan cepat: ada 100 kotak. Untuk tiap kotak, loop-nya membaca offsetHeight lalu langsung menulis style.height. Berapa kali browser menghitung layout: 1, 2, atau 100? Jawabannya ada di bagian "Baca dulu semua, baru tulis semua".

Baca dan tulis di DOM

Kode yang menyentuh DOM melakukan dua hal yang beda:

Kalau kamu membaca waktu layout-nya basi, browser terpaksa menghitung layout saat itu juga, di tengah kode JavaScript-mu. Ini namanya forced synchronous layout (layout yang dipaksa). Baca dan tulis yang selang-seling berkali-kali, sampai layout dipaksa terus, disebut layout thrashing.

Baca dulu semua, baru tulis semua

Contohnya tiga batang dengan tinggi 5, 10, dan 3 px. Tiap tinggi mau diubah jadi tinggi * 2 + 10, jadi hasilnya 20, 30, dan 16 px. Halamannya lagi tenang, jadi layout awalnya segar.

Langkah Selang-seling Batch
1 baca batang 1 (layout segar) baca batang 1
2 tulis batang 1, layout basi baca batang 2
3 baca batang 2: layout dipaksa baca batang 3
4 tulis batang 2, layout basi tulis batang 1
5 baca batang 3: layout dipaksa tulis batang 2
6 tulis batang 3 tulis batang 3
Layout dipaksa 2 kali 0 kali

Sesudah loop, dua-duanya butuh satu layout lagi waktu frame berikutnya digambar. Dengan 100 kotak, versi selang-seling memaksa layout 99 kali, ditambah 1 kali di frame berikutnya: total 100. Versi batch cuma 1. Itu jawaban tebakannya: 100 lawan 1.

// Selang-seling: tiap baca setelah tulis memaksa layout
const bars = [...document.querySelectorAll(".bar")]; // tinggi 5, 10, 3

bars.forEach((bar) => {
  const h = bar.offsetHeight;           // baca
  bar.style.height = h * 2 + 10 + "px"; // tulis
});
// Batch: baca semua dulu, baru tulis semua
const bars = [...document.querySelectorAll(".bar")];

const heights = bars.map((bar) => bar.offsetHeight); // [5, 10, 3]
bars.forEach((bar, i) => {
  bar.style.height = heights[i] * 2 + 10 + "px";     // 20, 30, 16
});

Latihan memodelkan pola ini tanpa DOM: tiap elemen jadi object { value }.

function batchUpdate(elements) {
  const values = elements.map((element) => element.value);   // 1. baca semua
  return values.map((value) => ({ value: value * 2 + 10 }));  // 2. tulis semua
}

console.log(batchUpdate([{ value: 5 }, { value: 10 }, { value: 3 }]));
// [{ value: 20 }, { value: 30 }, { value: 16 }]

Menambah banyak elemen sekaligus

Menambah 1.000 <li> satu per satu ke list yang tampil gak bikin 1.000 kali layout, selama di dalam loop gak ada yang membaca ukuran. Layout tetap ditunda sampai frame berikutnya. Tapi tiap elemen yang masuk ke halaman tetap menambah kerja kecil: browser mencatat perubahannya dan menandai style yang perlu dihitung ulang. DocumentFragment adalah wadah ringan yang hidup di memori, bukan di halaman. Kumpulkan semua elemen di situ, lalu masukkan ke halaman sekali.

const list = document.querySelector("#list");
const fragment = document.createDocumentFragment(); // wadah di memori

for (let i = 1; i <= 1000; i++) {
  const li = document.createElement("li");
  li.textContent = "Item " + i;
  fragment.appendChild(li); // halaman belum berubah
}

list.appendChild(fragment); // 1.000 <li> masuk sekali jalan

textContent atau innerHTML

Aturannya: data dari user selalu lewat textContent.

⚠️ Jebakan yang sering ketemu

TL;DR: Menulis ke DOM cuma menandai layout basi, dan browser menghitungnya sekali waktu menggambar. Membaca ukuran (offsetHeight, getBoundingClientRect()) setelah menulis memaksa layout saat itu juga, dan baca tulis yang selang-seling di loop disebut layout thrashing: 100 kotak jadi 100 kali layout. Baca semua dulu, baru tulis semua, dan layout-nya cuma sekali. Untuk banyak elemen baru, kumpulkan di DocumentFragment lalu masukkan sekali. Teks dari user pakai textContent, bukan innerHTML. Di latihan, batchUpdate membaca semua value, lalu me-return array baru berisi { value: value * 2 + 10 }.

Yang akan kamu pelajari