ResizeObserver: seperti document.onresize untuk elemen

ResizeObserver memberi tahu Anda saat ukuran elemen berubah.

Sebelum ResizeObserver, Anda harus melampirkan pemroses ke peristiwa resize dokumen untuk mendapatkan notifikasi tentang perubahan dimensi area tampilan. Di handler peristiwa, Anda harus mencari tahu elemen mana yang terpengaruh oleh perubahan tersebut dan memanggil rutin tertentu untuk bereaksi dengan tepat. Jika Anda memerlukan dimensi baru elemen setelah pengubahan ukuran, Anda harus memanggil getBoundingClientRect() atau getComputedStyle(), yang dapat menyebabkan thrashing tata letak jika Anda tidak berhati-hati dalam mengelompokkan semua pembacaan dan semua penulisan.

Hal ini bahkan tidak mencakup kasus saat elemen mengubah 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 elemen induknya.

Inilah alasan ResizeObserver menjadi primitif yang berguna. Objek ini bereaksi terhadap perubahan ukuran salah satu elemen yang diamati, terlepas dari penyebab perubahan tersebut. Hal ini juga memberikan akses ke ukuran baru elemen yang diamati.

Browser Support

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

Source

API

Semua API dengan akhiran Observer yang kami sebutkan di atas memiliki desain API yang sederhana. ResizeObserver tidak terkecuali. Anda membuat objek ResizeObserver dan meneruskan callback ke konstruktor. Callback meneruskan array objek ResizeObserverEntry—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 elemen melalui properti yang disebut contentRect, yang menampilkan objek DOMRectReadOnly. Kotak konten adalah kotak tempat konten dapat ditempatkan. Ini adalah kotak batas dikurangi padding.

Diagram model kotak CSS.

Penting untuk diperhatikan bahwa meskipun ResizeObserver melaporkan dimensi contentRect dan padding, ResizeObserver hanya memantau contentRect. Jangan keliru menganggap contentRect sebagai kotak pembatas elemen. Kotak pembatas, seperti yang dilaporkan oleh getBoundingClientRect(), adalah kotak yang berisi seluruh elemen dan turunannya. SVG adalah pengecualian untuk aturan ini, di mana 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 diamati pada saat callback dipanggil.

  • borderBoxSize
  • contentBoxSize
  • devicePixelContentBoxSize

Semua item ini menampilkan array hanya baca karena diharapkan pada masa mendatang item tersebut 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 properti pertama.

Kapan dilaporkan?

Spesifikasi menetapkan bahwa ResizeObserver harus memproses semua peristiwa pengubahan ukuran sebelum menggambar dan setelah tata letak. Hal ini membuat callback ResizeObserver menjadi tempat yang ideal untuk membuat perubahan pada tata letak halaman Anda. Karena pemrosesan ResizeObserver terjadi antara tata letak dan gambar, tindakan ini hanya akan membuat tata letak tidak valid, bukan gambar.

Oke

Anda mungkin bertanya-tanya: apa yang terjadi jika saya mengubah ukuran elemen yang diamati di dalam callback menjadi ResizeObserver? Jawabannya adalah: Anda akan segera memicu panggilan lain ke callback. Untungnya, ResizeObserver memiliki mekanisme untuk menghindari loop callback tanpa henti dan dependensi siklus. Perubahan hanya akan diproses dalam frame yang sama jika elemen yang diubah ukurannya lebih dalam di struktur DOM daripada elemen paling dangkal yang diproses dalam callback sebelumnya. Jika tidak, mereka akan ditunda ke frame berikutnya.

Aplikasi

Salah satu hal yang dapat Anda lakukan dengan ResizeObserver adalah menerapkan 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 yang dapat dilihat adalah jendela chat. Masalah yang muncul dalam tata letak percakapan atas ke bawah yang umum adalah posisi scroll. Untuk menghindari kebingungan pengguna, sebaiknya jendela tetap berada di bagian bawah percakapan, tempat pesan terbaru muncul. Selain itu, semua jenis perubahan tata letak (misalnya, ponsel beralih dari lanskap ke potret atau sebaliknya) harus mencapai hal yang sama.

ResizeObserver memungkinkan Anda menulis satu bagian kode tunggal yang menangani kedua skenario. Mengubah ukuran jendela adalah peristiwa yang dapat direkam oleh ResizeObserver berdasarkan definisi, tetapi memanggil appendChild() juga mengubah ukuran elemen tersebut (kecuali overflow: hidden ditetapkan), karena perlu menyediakan ruang untuk elemen baru. Dengan mempertimbangkan hal ini, hanya diperlukan beberapa 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, kan?

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

Kasus penggunaan lainnya adalah untuk semua jenis elemen kustom yang melakukan tata letaknya sendiri. Sebelum ResizeObserver, tidak ada cara yang andal untuk mendapatkan notifikasi saat dimensinya berubah sehingga turunannya dapat ditata ulang.

Efek pada Interaction to 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 tersebut merespons interaksi pengguna dengan andal.

Meskipun durasi yang diperlukan untuk menjalankan callback peristiwa sebagai respons terhadap interaksi pengguna dapat berkontribusi secara signifikan terhadap total latensi interaksi, bukan hanya itu aspek INP yang perlu dipertimbangkan. INP juga mempertimbangkan jumlah waktu yang diperlukan agar paint berikutnya dari interaksi terjadi. Ini adalah jumlah waktu yang diperlukan untuk menyelesaikan tugas rendering yang diperlukan untuk memperbarui antarmuka pengguna sebagai respons terhadap interaksi.

Untuk ResizeObserver, hal ini penting karena callback yang dijalankan instance ResizerObserver terjadi tepat sebelum tugas rendering. Hal ini sudah didesain demikian, karena pekerjaan yang terjadi dalam callback harus diperhitungkan, karena hasil pekerjaan tersebut kemungkinan besar akan memerlukan perubahan pada antarmuka pengguna.

Lakukan pekerjaan rendering sesedikit mungkin yang diperlukan dalam callback ResizeObserver, karena pekerjaan rendering yang berlebihan dapat menyebabkan browser tertunda dalam melakukan pekerjaan penting. Misalnya, jika ada interaksi yang memiliki callback yang menyebabkan callback ResizeObserver berjalan, pastikan Anda melakukan hal berikut untuk memfasilitasi pengalaman yang paling lancar:

  • Pastikan pemilih CSS Anda sesederhana mungkin untuk menghindari pekerjaan penghitungan ulang gaya yang berlebihan. Penghitungan ulang gaya terjadi tepat sebelum tata letak, dan selektor CSS yang kompleks dapat menunda operasi tata letak.
  • Hindari melakukan tugas apa pun dalam callback ResizeObserver yang dapat memicu penyusunan ulang paksa.
  • Waktu yang diperlukan untuk memperbarui tata letak halaman umumnya meningkat seiring dengan jumlah elemen DOM di halaman. Meskipun hal ini benar terlepas dari apakah halaman menggunakan ResizeObserver atau tidak, pekerjaan yang dilakukan dalam callback ResizeObserver dapat menjadi signifikan seiring meningkatnya kompleksitas struktural halaman.

Kesimpulan

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