Panel katalog toko Budi dirancang menyesuaikan jumlah kolom dengan ruang yang tersedia: tiga kolom pada panel lebar, dua pada sedang, satu pada sempit. Pertanyaannya, dari mana JavaScript tahu bahwa panel berubah ukuran? Event resize pada window tidak cukup — event itu hanya menyala saat jendela browser digeser ukurannya. Padahal panel yang sama ikut menyempit ketika sidebar keranjang dibuka, ketika font kustom selesai dimuat, atau ketika tata letak diubah oleh script lain. Perubahan ukuran elemen memiliki banyak pemicu, dan hampir semuanya tidak melewati window.

ResizeObserver mengamati elemennya langsung: callback kita dipanggil setiap kali elemen yang terdaftar berubah ukuran — apa pun penyebabnya. Browser menjalankan callback setelah tata letak dihitung dan sebelum penggambaran ke layar, sehingga penyesuaian yang kita tulis di dalamnya tampil serempak dengan perubahan ukuran itu sendiri.

Perlu ditegaskan perannya dibandingkan CSS. Bila keputusan tata letak bisa ditulis murni di CSS — media query, clamp(), grid otomatis — jangan pakai JavaScript; CSS menyelesaikannya tanpa satu baris script. ResizeObserver masuk untuk keputusan yang butuh logika: menghitung jumlah kolom dari rumus, mengatur ulang skala canvas, atau memberi tahu library pihak ketiga bahwa kontainernya berubah. Setiap callback yang tidak perlu tetap berarti kerja ekstra untuk mesin render.

Mengamati Ukuran Elemen #

Aturannya sama dengan observer lain yang sudah kita kenal: new ResizeObserver(callback) membuat pengamat, pengamat.observe(elemen) mendaftarkannya. Callback menerima array entries; setiap entry membawa ukuran terbaru elemennya. Berikut pengatur kolom panel katalog:

// pantau-panel.js — menyesuaikan jumlah kolom mengikuti lebar panel
// ResizeObserver hanya ada di browser;
// berkas ini lolos node --check dan dijalankan pada halaman katalog.

const panel = document.querySelector("#panel-katalog");

const pengamat = new ResizeObserver((entries) => {
  for (const entry of entries) {
    const lebar = entry.contentRect.width;
    const kolom = lebar > 600 ? 3 : lebar > 320 ? 2 : 1;
    panel.style.setProperty("--kolom", kolom);
    console.log(`Lebar ${Math.round(lebar)}px -> ${kolom} kolom`);
  }
});

pengamat.observe(panel);

Nilai --kolom dipakai CSS panel untuk menata grid-nya:

css
#panel-katalog {
  display: grid;
  grid-template-columns: repeat(var(--kolom, 1), 1fr);
}

ResizeObserver tidak tersedia di Node maupun jsdom, sehingga berkas ini diverifikasi sintaksnya dengan node --check pantau-panel.js; keluaran berikut dicatat sebagai keluaran console browser sesuai perilaku standar. Ketika panel berubah lebar dua kali — pertama ke 720 pixel, lalu ke 480 pixel — console mencatat:

Keluaran console browser:

text
Lebar 720px -> 3 kolom
Lebar 480px -> 2 kolom

Mari kita telaah baris-baris pentingnya:

  1. Baris 7 (new ResizeObserver(...)): callback menerima array entries — satu entry untuk setiap elemen yang ukurannya berubah pada frame tersebut. Sebagaimana IntersectionObserver, satu observer boleh melayani banyak elemen.
  2. Baris 9 (entry.contentRect.width): contentRect adalah kotak konten elemen dalam pixel — lebar tanpa padding dan border. Nilai ini yang paling relevan untuk keputusan tata letak seperti jumlah kolom.
  3. Baris 10 (pemilihan kolom): tiga ambang lebar diterjemahkan menjadi satu angka kolom. Logika keputusan sebaiknya tetap di JavaScript; CSS hanya menerima hasilnya lewat variabel.
  4. Baris 11 (panel.style.setProperty(...)): angka kolom ditulis sebagai CSS custom property. Perubahan gaya di dalam callback ResizeObserver diterapkan sebelum frame digambar — tidak ada kedipan tata letak yang terlihat pengunjung.
  5. Baris 16 (pengamat.observe(panel)): pendaftaran langsung memicu callback satu kali dengan ukuran saat itu, sehingga panel sudah tertata dengan benar sebelum ada perubahan apa pun.

contentRect dan Saatnya Callback Berjalan #

contentRect berupa object DOMRect dengan width, height, serta posisi x dan y. Namanya memuat batasan yang perlu diingat: yang dilaporkan adalah kotak konten — padding dan border tidak termasuk. Untuk kebutuhan yang menghitung box penuh, entry juga menyediakan entry.borderBoxSize dan entry.contentBoxSize berupa array berisi object { inlineSize, blockSize } (ukuran arah horizontal dan vertikal); bentuk array ini mempersiapkan penataan multi-kolom di masa depan.

Waktu pelaksanaannya juga khas: callback berjalan setelah layout selesai dan sebelum paint, dan perubahan ukuran yang terjadi pada frame yang sama digabung menjadi satu panggilan dengan beberapa entry. Browser juga melindungi diri dari callback yang mengubah ukuran elemen yang sedang diamati berulang-ulang — loop seperti itu dihentikan dan dilaporkan sebagai galat di console, jadi jangan menulis perubahan ukuran yang memantik perubahan ukuran lagi di dalam callback yang sama.

Mengelola dan Menyelaraskan Observer Lama #

Method unobserve(elemen) melepas satu target dan disconnect() menghentikan seluruh pemantauan — berguna saat panel katalog diganti halaman lain. Bila pemantauan dihentikan lalu dibutuhkan lagi, cukup panggil observe() pada object yang sama: pendaftaran ulang memicu callback sekali dengan ukuran terkini, sehingga penataan langsung tersinkron tanpa langkah manual. Untuk browser jauh lebih tua yang tidak menyediakan ResizeObserver, tersedia polyfill resmi yang meniru perilakunya dengan event resize dan polling terjadwal; muat polyfill itu sebelum berkas utama, dan sisanya tidak perlu berubah. Browser stabil pada tahun 2026 semuanya sudah menyediakan API ini sejak lama, sehingga polyfill hanya dipertimbangkan bila daftar dukungan browser produk kita masih memuatnya.

Dibandingkan dengan pasangan window + resize, keunggulan ResizeObserver terletak pada presisi sasaran: kita memantau panel yang benar-benar berubah, bukan menebak dari ukuran jendela. Tabel berikut merangkum bedanya:

AspekEvent resize pada windowResizeObserver
Pemicuperubahan ukuran jendela browser sajaperubahan ukuran setiap elemen terdaftar, apa pun penyebabnya
Sumber perubahan yang tertangkapsidebar, muat font, dan perubahan tata letak lain tidak terdeteksisemuanya terdeteksi, termasuk yang berasal dari script lain
Waktu pelaksanaansekitar saat perubahan jendelasetelah layout, sebelum paint — satu kali per frame yang relevan
Sasaranselalu seluruh halamanper elemen yang didaftarkan

Perbedaan waktu pelaksanaan itu punya dampak yang terasa. Callback ResizeObserver berjalan sebelum frame digambar, sehingga penyesuaian yang dilakukannya tidak pernah menampilkan keadaan setengah jadi; dibandingkan pemeriksaan ukuran di dalam handler resize yang jadwalnya tidak terikat siklus render, hasilnya konsisten dan tanpa kedipan.

Bersama IntersectionObserver di chapter sebelumnya, keduanya membentuk pola dasar pengamatan tampilan modern — satu untuk “kapan elemen terlihat”, satu untuk “seberapa besar elemen itu”. Ketiga pengamat browser (termasuk MutationObserver pada chapter berikutnya) berbagi kerangka yang sama: callback array entries, satu observer untuk banyak target, dan method observe/unobserve/disconnect yang namanya persis sama.

Berkas Sumber

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

pantau-panel.js

Unduh berkas
javascript
// pantau-panel.js — menyesuaikan jumlah kolom mengikuti lebar panel
// ResizeObserver hanya ada di browser;
// berkas ini lolos node --check dan dijalankan pada halaman katalog.

const panel = document.querySelector("#panel-katalog");

const pengamat = new ResizeObserver((entries) => {
  for (const entry of entries) {
    const lebar = entry.contentRect.width;
    const kolom = lebar > 600 ? 3 : lebar > 320 ? 2 : 1;
    panel.style.setProperty("--kolom", kolom);
    console.log(`Lebar ${Math.round(lebar)}px -> ${kolom} kolom`);
  }
});

pengamat.observe(panel);