CSS containment adalah cara memberi janji ke browser: "isi elemen ini berdiri sendiri, apa yang berubah di dalamnya gak memengaruhi bagian lain halaman." Dengan janji itu, browser boleh membatasi Layout dan Paint ke dalam elemen itu saja, bahkan melewatkan render elemen yang belum kelihatan. Bayangin gedung kantor. Tiap ruangan punya sekat, jadi menata ulang meja di satu ruangan gak bikin seluruh gedung ikut ditata. Lampunya pakai sensor: ruangan yang gak ada orangnya dibiarkan gelap, tapi denah gedung tetap mencatat ukurannya. Sekat itu contain, lampu sensor itu content-visibility: auto, dan catatan ukuran di denah itu contain-intrinsic-size.
🧪 Tebakan cepat: feed berisi 40 artikel, tiap artikel butuh sekitar 2 ms untuk di-render, dan layar cuma muat 3 artikel. Dengan
content-visibility: auto, render awalnya kira-kira berapa: 80 ms, 40 ms, atau 10 ms? Jawabannya ada di bagian "content-visibility: auto".
contain: janji ke browser
Di pelajaran CSS & Rendering Pipeline kamu sudah lihat bahwa Layout bisa merembet: satu elemen berubah ukuran, tetangga dan parent-nya ikut dihitung ulang. Property contain memotong rembetan itu. Nilainya:
| Nilai | Janjinya | Efek yang kelihatan |
|---|---|---|
layout |
layout di dalam elemen gak memengaruhi luar, dan sebaliknya | elemen jadi patokan untuk anak yang position: absolute atau fixed |
paint |
isinya gak pernah digambar di luar kotaknya | isi yang keluar kotak terpotong, dan kalau kotaknya di luar layar, isinya gak di-paint |
size |
ukuran elemen gak bergantung isinya | tanpa width dan height, tingginya jadi 0 |
style |
counter dan quotes di dalam gak bocor keluar | jarang berpengaruh ke performance |
Dua singkatan yang sering dipakai:
contain: contentsama denganlayout paint style. Aman untuk kebanyakan komponen, karena ukurannya tetap ikut isi.contain: strictsama dengansize layout paint style. Paling agresif, tapi elemennya wajib punya ukuran sendiri.
/* Widget chat di sidebar yang berubah tiap ada pesan baru */
.widget-chat {
contain: content; /* layout + paint + style */
}
/* Kanvas game dengan ukuran pasti */
.papan-game {
width: 320px;
height: 240px;
contain: strict; /* size + layout + paint + style */
}
contain: contentdi widget chat: tiap pesan baru, Layout dan Paint cukup dihitung di dalam widget, gak merembet ke artikel di sebelahnya.contain: strictbutuhwidthdanheight, karena dengansizebrowser gak lagi melihat isi untuk menentukan ukurannya.contain: layout paintartinya elemen itu terisolasi: perubahan di dalamnya gak memengaruhi layout dan paint elemen di luar.
content-visibility: auto
content-visibility: auto melangkah lebih jauh: selama elemen jauh dari layar, browser gak me-render isinya sama sekali. Style, Layout, dan Paint isinya dilewati sampai elemen itu mendekati layar.
Contoh di Lab: feed 40 artikel, tiap artikel tinggi 80 px, layar setinggi 240 px (muat 3 artikel). Browser juga menyiapkan sedikit area di sekitar layar, di Lab setengah tinggi layar, 120 px.
| Artikel dirender di awal | Waktu render (2 ms per artikel) | |
|---|---|---|
tanpa content-visibility |
40 | 80 ms |
dengan content-visibility: auto |
5 (3 di layar, 2 di dekatnya) | 10 ms |
Itu jawaban tebakannya: sekitar 10 ms. 35 artikel lainnya dilewati, dan baru di-render waktu kamu scroll mendekatinya. Angka 2 ms itu simulasi Lab, tapi polanya nyata: di contoh web.dev, render awal halaman blog turun dari 232 ms jadi 30 ms.
.artikel {
content-visibility: auto;
contain-intrinsic-size: auto 80px; /* perkiraan tinggi selama dilewati */
}
content-visibility: autoselalu memasang containmentlayout,paint, danstyle, lalu menambahsizeselama elemennya dilewati.- Karena ada
size, artikel yang dilewati tingginya jadi 0.contain-intrinsic-size: auto 80pxmemberinya tinggi pengganti 80 px, jadi total tinggi halaman tetap 40 × 80 = 3.200 px dari awal. - Kata
autodi situ bikin browser mengingat tinggi asli artikel setelah sekali di-render, dan memakainya lagi waktu artikel itu dilewati lagi. - Isinya tetap ada di DOM: Ctrl+F dan pembaca layar (screen reader) tetap bisa menemukannya. Beda dengan
display: none. - Sudah didukung semua browser utama. Safari menyusul paling akhir, di versi 18 (2024).
Tanpa contain-intrinsic-size, artikel yang dilewati tingginya 0 px, jadi tinggi halaman gak pernah tetap: Chromium terus mengukurnya ulang setiap kali ada artikel yang mulai atau berhenti di-render. Waktu kamu scroll, artikel baru di-render, tingginya muncul, dan scrollbar meloncat-loncat.
Kapan pakai yang mana
- Daftar atau halaman panjang (feed, komentar, artikel dengan banyak bagian):
content-visibility: autopluscontain-intrinsic-size. - Widget mandiri yang sering berubah (chat, harga saham, jam):
contain: content. - Komponen berukuran pasti yang benar-benar mandiri (kanvas game, pemutar video berukuran tetap):
contain: strict. - Elemen di layar pertama (header, hero): gak perlu. Elemen itu memang harus langsung di-render, jadi gak ada yang bisa dilewati.
⚠️ Jebakan yang sering ketemu
contain: strictatausizetanpa ukuran. Kotaknya kolaps jadi 0 tinggi dan isinya meluber atau terpotong.content-visibility: autotanpacontain-intrinsic-size. Artikel yang dilewati dihitung 0 px, jadi scrollbar meloncat dan konten bisa bergeser waktu di-scroll.contain: paintdi elemen yang punya dropdown atau tooltip. Menu yang keluar dari kotaknya ikut terpotong.- Memasang containment di mana-mana. Untungnya cuma terasa di bagian yang besar atau sering berubah. Di kotak kecil yang diam, gak ada bedanya.
TL;DR: contain adalah janji bahwa isi elemen berdiri sendiri: layout dan paint membatasi Layout dan Paint ke dalam kotaknya, size bikin ukurannya gak bergantung isi, content sama dengan layout paint style, dan strict (paling agresif) sama dengan size layout paint style sehingga wajib diberi ukuran. content-visibility: auto melewatkan render isi elemen yang jauh dari layar: di Lab, feed 40 artikel cuma me-render 5 di awal, 10 ms bukan 80 ms. Selalu pasangkan dengan contain-intrinsic-size: auto 80px (tinggi perkiraan) supaya scrollbar gak meloncat.