ResizeObserver: seperti document.onresize untuk elemen

ResizeObserver memberi tahu Anda saat ukuran elemen berubah.

Sebelum ResizeObserver, Anda harus melampirkan pemroses ke resize dokumen untuk mendapatkan notifikasi tentang setiap perubahan dimensi area pandang. Di acara Anda kemudian harus mencari tahu elemen mana yang telah dipengaruhi oleh perubahan itu dan memanggil rutinitas tertentu untuk bereaksi dengan tepat. Jika Anda perlu dimensi baru dari suatu elemen setelah mengubah ukuran, Anda harus memanggil getBoundingClientRect() atau getComputedStyle(), yang dapat menyebabkan tata letak thrashing jika Anda tidak mengurus pengelompokan semua pembacaan dan semua operasi tulis.

Ini bahkan tidak mencakup kasus di mana elemen mengubah ukurannya tanpa ukuran jendela telah diubah. Misalnya, menambahkan turunan baru, mengatur gaya display elemen menjadi none, atau tindakan serupa dapat mengubah ukuran elemen, saudara kandung, atau nenek moyangnya.

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

Dukungan Browser

  • Chrome: 64.
  • Edge: 79.
  • Firefox: 69.
  • Safari: 13.1.

Sumber

API

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

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 suatu elemen melalui properti yang disebut contentRect, yang menampilkan DOMRectReadOnly . Kotak konten adalah kotak tempat konten dapat ditempatkan. Penting kotak pembatas tanpa {i>padding<i}.

Diagram model kotak CSS.

Penting untuk diperhatikan bahwa meskipun ResizeObserver melaporkan kedua dimensi contentRect dan padding, hanya mengawasi contentRect. Jangan keliru antara contentRect dengan kotak pembatas elemen. Pembatas kotak, seperti yang dilaporkan oleh getBoundingClientRect(), adalah kotak yang berisi seluruh elemen dan turunannya. SVG merupakan pengecualian dari aturan tersebut, dengan ResizeObserver akan melaporkan dimensi kotak pembatas.

Mulai Chrome 84, ResizeObserverEntry memiliki tiga properti baru untuk memberikan lebih banyak properti informasi terperinci. Setiap properti ini menampilkan ResizeObserverSize objek yang berisi properti blockSize dan properti inlineSize. Ini informasi tentang elemen yang diamati pada saat callback dipanggil.

  • borderBoxSize
  • contentBoxSize
  • devicePixelContentBoxSize

Semua item ini mengembalikan {i>array<i} {i>read-only<i} karena di masa mendatang diharapkan mendukung elemen yang memiliki beberapa fragmen, yang terjadi di 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 dilaporkan?

Spesifikasi melarang ResizeObserver harus memproses semua peristiwa perubahan ukuran sebelum menggambar dan setelah tata letak. Hal ini membuat callback ResizeObserver menjadi tempat yang ideal untuk membuat perubahan pada tata letak halaman. Karena ResizeObserver pemrosesan terjadi antara layout dan paint, hal itu hanya akan membatalkan tata letak, bukan cat.

Oke

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

Aplikasi

Satu hal yang memungkinkan Anda lakukan ResizeObserver adalah mengimplementasikan setiap elemen terhadap kueri media. Dengan mengamati elemen, Anda dapat secara imperatif menentukan mendesain {i>breakpoint<i} dan mengubah gaya sebuah elemen. Dalam example, kotak kedua akan mengubah radius batas 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 dari atas ke bawah yang biasa adalah pemosisian scroll. Untuk menghindari membingungkan pengguna, akan sangat membantu jika jendela menempel di bagian bawah percakapan, tempat pesan terbaru muncul. Selain itu, segala jenis tata letak perubahan (misalnya ponsel yang beralih dari lanskap ke potret atau sebaliknya) seharusnya mencapai hal yang sama.

ResizeObserver memungkinkan Anda menulis satu kode yang menangani kedua skenario. Mengubah ukuran jendela adalah peristiwa yang dapat dilakukan oleh ResizeObserver tangkap menurut definisi, tetapi memanggil appendChild() juga akan mengubah ukuran elemen tersebut (kecuali overflow: hidden disetel), karena perlu memberikan ruang untuk file baru yang kurang penting. Dengan mengingat hal ini, dibutuhkan sangat sedikit baris untuk mencapai tujuan yang efek:

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, kan?

Dari sini, saya dapat menambahkan lebih banyak kode untuk menangani kasus di mana pengguna menggulir secara manual dan ingin men-scroll untuk menempelkan pesan tersebut saat pesan baru akan diperlukan.

Kasus penggunaan lainnya adalah untuk segala jenis elemen kustom yang melakukan tata letak sendiri. Hingga ResizeObserver, tidak ada cara yang dapat diandalkan untuk mendapatkan notifikasi saat dimensi berubah sehingga turunannya dapat ditata lagi.

Efek pada Interaksi dengan Gambar Berikutnya (INP)

Interaction to Next Paint (INP) adalah metrik yang mengukur responsivitas halaman secara keseluruhan terhadap interaksi pengguna. Jika INP halaman ada dalam “bagus” nilai minimum—yaitu, 200 dalam milidetik atau kurang—dapat dikatakan bahwa sebuah laman dapat responsif terhadap interaksi pengguna dengannya.

Sementara jumlah waktu yang dibutuhkan callback peristiwa untuk berjalan sebagai respons terhadap interaksi pengguna dapat berkontribusi signifikan terhadap total latensi interaksi, aspek ini bukan satu-satunya aspek INP yang perlu dipertimbangkan. INP juga mempertimbangkan jumlah waktu yang diperlukan untuk pelukisan berikutnya dari interaksi. Ini adalah jumlah waktu yang dibutuhkan untuk pekerjaan rendering yang diperlukan untuk memperbarui pengguna sebagai respons terhadap interaksi yang akan diselesaikan.

Jika ResizeObserver diperhatikan, hal ini penting karena callback yang instance ResizerObserver yang berjalan terjadi tepat sebelum pekerjaan rendering. Ini memang didesain, karena pekerjaan yang terjadi di callback harus dilakukan karenanya, kemungkinan besar tindakan tersebut akan memerlukan perubahan API melalui antarmuka pengguna grafis.

Lakukan tugas rendering sesedikit yang diperlukan dalam ResizeObserver , karena proses rendering yang berlebihan dapat menimbulkan situasi ketika browser terlambat dalam melakukan pekerjaan penting. Misalnya, jika ada interaksi yang yang menyebabkan callback ResizeObserver berjalan, pastikan Anda melakukan berikut untuk memfasilitasi pengalaman yang selancar mungkin:

  • Pastikan pemilih CSS Anda sesederhana mungkin untuk menghindari penghitungan ulang gaya yang berlebihan. Penghitungan ulang gaya terjadi sebelum tata letak, dan pemilih CSS yang kompleks bisa menunda operasi tata letak.
  • Hindari melakukan tugas apa pun di callback ResizeObserver yang dapat memicu mengubah posisi/geometri secara paksa.
  • Waktu yang dibutuhkan untuk memperbarui tata letak laman biasanya bertambah seiring dengan jumlah elemen DOM di suatu halaman. Meski hal ini berlaku, apakah halaman menggunakan ResizeObserver, pekerjaan yang dilakukan di callback ResizeObserver dapat menjadi signifikan seiring dengan meningkatnya kompleksitas struktural halaman.

Kesimpulan

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