ResizeObserver: seperti document.onresize untuk elemen

ResizeObserver memberi tahu Anda saat ukuran elemen berubah.

Sebelum ResizeObserver, Anda harus menambahkan pemroses ke peristiwa resize dokumen agar mendapatkan notifikasi terkait perubahan apa pun pada dimensi area pandang. Dalam pengendali peristiwa, Anda harus mencari tahu elemen mana yang terpengaruh oleh perubahan tersebut dan memanggil rutinitas tertentu untuk bereaksi dengan tepat. Jika memerlukan dimensi baru elemen setelah perubahan ukuran, Anda harus memanggil getBoundingClientRect() atau getComputedStyle(), yang dapat menyebabkan layout thrashing jika Anda tidak mengurus batch semua operasi baca dan semua penulisan.

Hal ini bahkan tidak mencakup kasus saat elemen berubah ukurannya tanpa mengubah ukuran jendela utama. Misalnya, menambahkan turunan baru, menyetel gaya display elemen ke none, atau tindakan serupa dapat mengubah ukuran elemen, pasangannya, atau ancestornya.

Inilah alasan ResizeObserver adalah primitif yang berguna. Sistem ini bereaksi terhadap perubahan ukuran elemen apa pun yang diamati, terlepas dari penyebab perubahan. Fungsi ini juga memberikan akses ke ukuran baru dari elemen yang diamati.

Dukungan Browser

  • 64
  • 79
  • 69
  • 13.1

Sumber

API

Semua API dengan akhiran Observer yang kami sebutkan di atas memiliki desain API yang sederhana. Begitu juga dengan ResizeObserver. Anda membuat objek ResizeObserver dan meneruskan callback ke konstruktor. Callback mendapatkan array objek ResizeObserverEntry—satu entri per elemen yang diamati—yang berisi dimensi baru untuk elemen tersebut.

var ro = new ResizeObserver(entries => {
  for (let entry of entries) {
    const cr = entry.contentRect;

    console.log('Element:', entry.target);
    console.log(`Element size: ${cr.width}px x ${cr.height}px`);
    console.log(`Element padding: ${cr.top}px ; ${cr.left}px`);
  }
});

// Observe one or multiple elements
ro.observe(someElement);

Beberapa detail

Apa yang dilaporkan?

Umumnya, ResizeObserverEntry melaporkan kotak konten elemen melalui properti yang disebut contentRect, yang menampilkan objek DOMRectReadOnly. Kotak konten adalah kotak tempat konten dapat ditempatkan. Kotak ini adalah kotak batas tanpa padding.

Diagram model kotak CSS.

Penting untuk diperhatikan bahwa meskipun ResizeObserver melaporkan dimensi contentRect dan padding, ResizeObserver hanya menonton contentRect. Jangan salah membedakan contentRect dengan kotak pembatas elemen. Kotak pembatas, seperti yang dilaporkan oleh getBoundingClientRect(), adalah kotak yang berisi seluruh elemen dan turunannya. SVG merupakan pengecualian untuk aturan ini, dengan ResizeObserver akan melaporkan dimensi kotak pembatas.

Mulai Chrome 84, ResizeObserverEntry memiliki tiga properti baru untuk memberikan informasi yang lebih mendetail. Setiap properti ini menampilkan objek ResizeObserverSize yang berisi properti blockSize dan properti inlineSize. Informasi ini adalah tentang elemen yang diobservasi pada saat callback dipanggil.

  • borderBoxSize
  • contentBoxSize
  • devicePixelContentBoxSize

Semua item ini menampilkan array hanya baca karena di masa mendatang diharapkan dapat mendukung elemen yang memiliki beberapa fragmen, yang terjadi dalam skenario multi-kolom. Untuk saat ini, array ini hanya akan berisi satu elemen.

Dukungan platform untuk properti ini terbatas, tetapi Firefox sudah mendukung dua yang pertama.

Kapan hal tersebut dilaporkan?

Spesifikasi ini menetapkan bahwa ResizeObserver harus memproses semua peristiwa perubahan ukuran sebelum menggambar dan setelah tata letak. Hal ini menjadikan callback ResizeObserver sebagai tempat yang ideal untuk membuat perubahan pada tata letak halaman. Karena pemrosesan ResizeObserver terjadi antara tata letak dan paint, tindakan tersebut hanya akan membatalkan tata letak, bukan paint.

Kena

Anda mungkin bertanya-tanya: apa yang terjadi jika saya mengubah ukuran elemen yang diamati di dalam callback ke ResizeObserver? Jawabannya adalah: Anda akan segera memicu panggilan lain ke callback. Untungnya, ResizeObserver memiliki mekanisme untuk menghindari loop callback yang tidak terbatas dan dependensi siklik. Perubahan hanya akan diproses dalam frame yang sama jika elemen yang diubah ukurannya berada lebih dalam di hierarki DOM daripada elemen shallowest yang diproses di callback sebelumnya. Jika tidak, {i>frame<i} akan dialihkan ke {i>frame<i} berikutnya.

Aplikasi

Satu hal yang diizinkan oleh ResizeObserver untuk Anda lakukan adalah mengimplementasikan kueri media per elemen. Dengan mengamati elemen, Anda dapat secara imperatif menentukan titik henti sementara desain dan mengubah gaya elemen. Dalam contoh berikut, kotak kedua akan mengubah radius batasnya sesuai dengan lebarnya.

const ro = new ResizeObserver(entries => {
  for (let entry of entries) {
    entry.target.style.borderRadius =
        Math.max(0, 250 - entry.contentRect.width) + 'px';
  }
});
// Only observe the second box
ro.observe(document.querySelector('.box:nth-child(2)'));

Contoh menarik lainnya untuk dilihat adalah jendela {i>chat<i}. Masalah yang muncul dalam tata letak percakapan umum dari atas ke bawah adalah posisi scroll. Agar tidak membingungkan pengguna, sebaiknya jendela menempel di bagian bawah percakapan, tempat pesan terbaru muncul. Selain itu, semua jenis perubahan tata letak (misalnya ponsel dari lanskap ke potret atau sebaliknya) akan mencapai hasil yang sama.

ResizeObserver memungkinkan Anda menulis satu kode yang menangani kedua skenario. Mengubah ukuran jendela adalah peristiwa yang dapat ditangkap ResizeObserver berdasarkan definisi, tetapi memanggil appendChild() juga akan mengubah ukuran elemen tersebut (kecuali jika overflow: hidden disetel), karena perlu memberi ruang untuk elemen baru. Dengan mempertimbangkan hal ini, diperlukan sangat sedikit baris untuk mencapai efek yang diinginkan:

const ro = new ResizeObserver(entries => {
  document.scrollingElement.scrollTop =
    document.scrollingElement.scrollHeight;
});

// Observe the scrollingElement for when the window gets resized
ro.observe(document.scrollingElement);

// Observe the timeline to process new messages
ro.observe(timeline);

Cukup rapi, bukan?

Dari sini, saya dapat menambahkan lebih banyak kode untuk menangani kasus ketika pengguna men-scroll ke atas secara manual dan ingin men-scroll agar tetap berpegang pada pesan tersebut saat pesan baru masuk.

Kasus penggunaan lain adalah untuk segala jenis elemen kustom yang membuat tata letaknya sendiri. Hingga ResizeObserver, tidak ada cara yang efektif untuk mendapatkan notifikasi saat dimensinya berubah sehingga turunannya dapat ditata lagi.

Efek pada Interaksi terhadap Next Paint (INP)

Interaction to Next Paint (INP) adalah metrik yang mengukur responsivitas keseluruhan halaman terhadap interaksi pengguna. Jika INP halaman berada dalam batas "baik"—yaitu, 200 milidetik atau kurang—dapat dikatakan bahwa halaman sangat responsif terhadap interaksi pengguna dengan halaman tersebut.

Meskipun jumlah waktu yang diperlukan callback peristiwa untuk dijalankan sebagai respons terhadap interaksi pengguna dapat berkontribusi secara signifikan terhadap total latensi interaksi, tetapi itu bukan satu-satunya aspek INP yang perlu dipertimbangkan. INP juga mempertimbangkan jumlah waktu yang diperlukan untuk melakukan tampilan berikutnya dari interaksi. Ini adalah jumlah waktu yang diperlukan untuk pekerjaan rendering yang diperlukan untuk mengupdate antarmuka pengguna sebagai respons terhadap interaksi untuk diselesaikan.

Jika ResizeObserver terkait, hal ini penting karena callback yang dijalankan instance ResizerObserver terjadi tepat sebelum pekerjaan rendering. Hal ini adalah desain, karena pekerjaan yang terjadi di callback harus diperhitungkan, sehingga pekerjaan tersebut kemungkinan besar memerlukan perubahan pada antarmuka pengguna.

Berhati-hatilah dalam melakukan pekerjaan rendering seminimal yang diperlukan dalam callback ResizeObserver, karena pekerjaan rendering yang berlebihan dapat menimbulkan situasi ketika browser tertunda dalam melakukan pekerjaan penting. Misalnya, jika interaksi memiliki callback yang menyebabkan callback ResizeObserver berjalan, pastikan Anda melakukan hal berikut untuk memfasilitasi pengalaman yang selancar mungkin:

  • Pastikan pemilih CSS Anda sesederhana mungkin untuk menghindari pekerjaan penghitungan ulang gaya yang berlebihan. Penghitungan ulang gaya terjadi tepat sebelum tata letak, dan pemilih CSS yang kompleks dapat menunda operasi tata letak.
  • Hindari melakukan pekerjaan apa pun di callback ResizeObserver yang dapat memicu perubahan posisi/geometri yang dipaksakan.
  • Waktu yang diperlukan untuk memperbarui tata letak halaman umumnya akan meningkat seiring dengan jumlah elemen DOM di halaman. Meskipun hal ini berlaku baik halaman menggunakan ResizeObserver atau tidak, pekerjaan yang dilakukan di callback ResizeObserver dapat menjadi signifikan saat kompleksitas struktural halaman meningkat.

Kesimpulan

ResizeObserver tersedia di semua browser utama dan menyediakan cara yang efisien untuk memantau perubahan ukuran elemen pada tingkat elemen. Berhati-hatilah agar tidak menunda rendering terlalu banyak dengan API canggih ini.