CSS murni jarang menjadi penyebab utama pekerjaan layout yang berlebihan — pelakunya biasanya pola JavaScript yang membaca ukuran elemen lalu menulis gaya, bergantian berkali-kali dalam satu putaran. Setiap pergantian dari membaca ke menulis memaksa browser menghitung ulang layout terlebih dahulu agar angka yang dibaca tetap akurat; pengulangan pola ini pada ratusan elemen dikenal sebagai layout thrashing . Pada Chapter ini kita melihat pola yang bermasalah, lalu memperbaikinya dengan pengelompokan: semua bacaan lebih dulu, semua penulisan menyusul.
Pola yang Bermasalah: Baca-Tulis Bergantian #
Misalnya kita ingin menandai elemen yang lebih tinggi dari 80px. Cara yang tampak wajar justru mahal:
// Pola mahal: layout dihitung ulang pada setiap iterasi
for (const el of daftarKartu) {
const tinggi = el.getBoundingClientRect().height; // baca
if (tinggi > 80) {
el.classList.add("menonjol"); // tulis
}
}Mari kita telaah mengapa pola ini mahal:
- Iterasi 1 (baca): layout belum kotor, jadi
getBoundingClientRecttidak perlu menghitung ulang — angka siap. - Iterasi 1 (tulis):
classList.addmengubah gaya dan membuat layout menjadi kotor (dirty) — tetapi browser belum menghitungnya; pekerjaan itu ditunda. - Iterasi 2 (baca): layout masih kotor dari penulisan sebelumnya, sehingga
getBoundingClientRectdipaksa menghitung ulang layout seluruh dokumen sekarang juga. - Iterasi berikutnya: siklus paksa-baca-ulang berulang, sekali per elemen — pada 200 kartu, berarti ratusan perhitungan layout dalam satu frame.
Pola ini paling sering muncul di dua tempat. Pertama pada handler yang berjalan terus-menerus — scroll dan resize membaca ukuran elemen setiap kejadian, lalu menulis gaya sebagai tanggapannya. Kedua pada kode yang menyusun ukuran secara berurutan: menata posisi tooltip, menyamakan tinggi kartu, atau menyesuaikan lebar kolom berdasarkan isi.
Pada panel Performance DevTools, kejadian ini tampil sebagai panggilan berlabel Forced reflow (recalculate style + layout) yang muncul berulang di dalam satu frame. Nama lainnya adalah layout thrashing: layout “dikibaskan” bolak-balik berkali-kali sebelum frame sempat digambar.
Perbaikan: Kelompokkan Bacaan dan Penulisan #
Prinsipnya satu kalimat: pisahkan fase baca dari fase tulis. Kumpulkan semua pengukuran lebih dulu, simpan angkanya, baru terapkan semua perubahan gaya:
// Pola hemat: satu siklus layout untuk seluruh daftar
const tinggiTercatat = daftarKartu.map(el => el.getBoundingClientRect().height);
daftarKartu.forEach((el, i) => {
if (tinggiTercatat[i] > 80) {
el.classList.add("menonjol");
}
});Pada versi ini, penulisan pertama terjadi setelah semua bacaan selesai, sehingga hanya ada satu perhitungan layout di akhir — bukan satu per elemen. Bila penulisan tidak mendesak dilakukan seketika, jadwalkan pada frame berikutnya lewat requestAnimationFrame agar tumpang tindih dengan siklus render, bukan menimpanya:
requestAnimationFrame(() => {
for (const el of daftarKartu) {
el.style.height = tinggiMaks + "px";
}
});Daftar metode yang dihitung sebagai “baca” patut dihafal: getBoundingClientRect(), offsetWidth/offsetHeight, clientWidth/clientHeight, getComputedStyle(), serta membaca scrollTop. Semua properti ini memicu layout sinkron bila layout sedang kotor. Penulisan apa pun — style.width, classList.add, mengganti teks — membuat layout kotor kembali.
Membuktikannya pada Demo #
Demo chapter ini menerapkan pola batch untuk menyamakan tinggi tiga kotak: skrip membaca tinggi ketiganya lebih dulu, mencari nilai maksimum, lalu menulis tinggi tersebut ke semua kotak dalam satu fase tulis yang dijadwalkan lewat requestAnimationFrame.
/* Gaya baris kotak untuk demo batch baca-tulis Chapter Avoiding Layout Thrashing.
Tinggi awal sengaja dibuat berbeda agar pengukuran dan penyamaan terlihat. */
.baris {
display: flex;
gap: 12px;
}
.kotak {
width: 180px;
padding: 12px;
background: rgb(255, 255, 255);
border: 1px solid rgb(203, 213, 225);
border-radius: 8px;
}
.kotak-pendek { height: 56px; }
.kotak-tinggi { height: 88px; }
.kotak-sedang { height: 72px; }Hasil di Browser:
Baris kotak tersusun berdampingan dengan display: flex dan gap: 12px.
Setelah fase baca (56px, 88px, 72px) dan satu fase tulis, ketiga kotak
berakhir dengan tinggi computed 88px pada lebar 180px — satu siklus
layout untuk seluruh operasi, ditampilkan bersama baris status.Uji manual: buka panel Performance, rekam saat halaman dimuat, dan pastikan hanya ada satu blok layout untuk skrip ini. Untuk melihat cara membaca rekaman itu — serta metrik paint yang menyertainya — lanjutkan ke Chapter Measuring CSS Performance .
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
contoh-batch.css
Unduh berkas/* Gaya baris kotak untuk demo batch baca-tulis Chapter Avoiding Layout Thrashing.
Tinggi awal sengaja dibuat berbeda agar pengukuran dan penyamaan terlihat. */
.baris {
display: flex;
gap: 12px;
}
.kotak {
width: 180px;
padding: 12px;
background: rgb(255, 255, 255);
border: 1px solid rgb(203, 213, 225);
border-radius: 8px;
}
.kotak-pendek { height: 56px; }
.kotak-tinggi { height: 88px; }
.kotak-sedang { height: 72px; }