Singkatnya: Gunakan kembali elemen DOM Anda dan hapus elemen yang jauh dari area tampilan. Gunakan placeholder untuk memperhitungkan data yang tertunda. Berikut adalah demo dan kode untuk penggeser tanpa batas.
Penggeser tanpa batas muncul di seluruh internet. Daftar artis Google Music adalah satu, linimasa Facebook adalah satu, dan feed live Twitter juga satu. Anda men-scroll ke bawah dan sebelum mencapai bagian bawah, konten baru muncul secara ajaib seolah-olah dari antah berantah. Pengalaman ini lancar bagi pengguna dan mudah untuk melihat daya tariknya.
Namun, tantangan teknis di balik peng-scroll tak terbatas lebih sulit daripada yang terlihat. Rentang masalah yang Anda hadapi saat ingin Melakukan Hal yang Benar™ sangat luas. Hal ini dimulai dengan hal-hal sederhana seperti link di footer yang hampir tidak dapat dijangkau karena konten terus mendorong footer menjauh. Namun, masalahnya menjadi lebih sulit. Bagaimana cara menangani peristiwa pengubahan ukuran saat seseorang memutar ponsel dari mode potret ke lanskap atau bagaimana cara mencegah ponsel Anda berhenti secara tiba-tiba saat daftar terlalu panjang?
The right thing™
Kami merasa itu sudah cukup menjadi alasan untuk membuat penerapan referensi yang menunjukkan cara mengatasi semua masalah ini dengan cara yang dapat digunakan kembali sekaligus mempertahankan standar performa.
Kita akan menggunakan 3 teknik untuk mencapai tujuan kita: daur ulang DOM, penanda dan penambatan scroll.
Contoh kasus kita adalah jendela chat seperti Hangouts tempat kita dapat men-scroll pesan. Hal pertama yang kita butuhkan adalah sumber pesan chat yang tidak terbatas. Secara teknis, tidak ada penggeser tak terbatas yang benar-benar tak terbatas, tetapi dengan jumlah data yang tersedia untuk dimasukkan ke dalam penggeser ini, penggeser tersebut mungkin sama saja. Demi kesederhanaan, kita akan meng-hardcode sekumpulan pesan chat dan memilih pesan, penulis, serta lampiran gambar sesekali secara acak dengan sedikit penundaan buatan agar berperilaku sedikit lebih seperti jaringan nyata.

Daur ulang DOM
Daur ulang DOM adalah teknik yang kurang dimanfaatkan untuk menjaga jumlah node DOM tetap rendah. Ide umumnya adalah menggunakan elemen DOM yang sudah dibuat dan berada di luar layar, bukan membuat elemen baru. Memang, node DOM itu sendiri murah, tetapi tidak gratis, karena setiap node menambahkan biaya ekstra dalam memori, tata letak, gaya, dan gambar. Perangkat kelas bawah akan terasa lebih lambat jika tidak dapat digunakan sama sekali jika situs memiliki DOM yang terlalu besar untuk dikelola. Perlu diingat juga bahwa setiap tata ulang dan penerapan ulang gaya Anda – proses yang dipicu setiap kali class ditambahkan atau dihapus dari node – akan menjadi lebih mahal dengan DOM yang lebih besar. Mendaur ulang node DOM berarti kita akan menjaga jumlah total node DOM jauh lebih rendah, sehingga semua proses ini menjadi lebih cepat.
Hambatan pertama adalah scrolling itu sendiri. Karena kita hanya akan memiliki subkumpulan kecil dari semua item yang tersedia di DOM pada waktu tertentu, kita perlu menemukan cara lain agar scrollbar browser mencerminkan jumlah konten yang secara teoretis ada dengan benar. Kita akan menggunakan elemen sentinel 1 px x 1 px dengan transformasi untuk memaksa elemen yang berisi item – runway – memiliki tinggi yang diinginkan. Kami akan mempromosikan setiap elemen di runway ke lapisannya sendiri untuk memastikan lapisan runway itu sendiri benar-benar kosong. Tidak ada warna latar belakang, tidak ada. Jika lapisan runway tidak kosong, lapisan tersebut tidak memenuhi syarat untuk pengoptimalan browser dan kita harus menyimpan tekstur di kartu grafis yang memiliki tinggi beberapa ratus ribu piksel. Tentu tidak dapat digunakan di perangkat seluler.
Setiap kali men-scroll, kita akan memeriksa apakah viewport sudah cukup dekat dengan ujung landasan pacu. Jika demikian, kita akan memperpanjang jalur dengan memindahkan elemen sentinel dan memindahkan item yang telah keluar dari area tampilan ke bagian bawah jalur dan mengisinya dengan konten baru.
Hal yang sama berlaku untuk men-scroll ke arah lain. Namun, kita tidak akan pernah memperkecil jalur dalam penerapan kita, sehingga posisi scrollbar tetap konsisten.
Tombstone
Seperti yang kami sebutkan sebelumnya, kami mencoba membuat sumber data kami berperilaku seperti sesuatu di dunia nyata. Dengan latensi jaringan dan semuanya. Artinya, jika pengguna kami menggunakan scroll cepat, mereka dapat dengan mudah men-scroll melewati elemen terakhir yang datanya kami miliki. Jika hal itu terjadi, kita akan menempatkan item penanda – placeholder – yang akan digantikan oleh item dengan konten sebenarnya setelah data tiba. Tombstone juga didaur ulang dan memiliki kumpulan terpisah untuk elemen DOM yang dapat digunakan kembali. Kita memerlukannya agar dapat melakukan transisi yang baik dari tanda nisan ke item yang diisi dengan konten, yang jika tidak dilakukan akan sangat mengganggu pengguna dan bahkan dapat membuat mereka kehilangan fokus pada apa yang sedang mereka perhatikan.

Tantangan menarik di sini adalah bahwa item sebenarnya dapat memiliki tinggi yang lebih besar daripada item batu nisan karena jumlah teks per item yang berbeda atau gambar yang dilampirkan. Untuk mengatasi hal ini, kita akan menyesuaikan posisi scroll saat ini setiap kali data masuk dan penanda diganti di atas area tampilan, menambatkan posisi scroll ke elemen, bukan nilai piksel. Konsep ini disebut penambatan scroll.
Arah scroll
Penambatan scroll kami akan dipanggil saat penanda diganti dan saat jendela diubah ukurannya (yang juga terjadi saat perangkat dibalik). Kita harus mencari tahu elemen yang paling atas dan terlihat di area tampilan. Karena elemen tersebut hanya dapat terlihat sebagian, kita juga akan menyimpan offset dari bagian atas elemen tempat area tampilan dimulai.

Jika ukuran area tampilan diubah dan jalur memiliki perubahan, kita dapat memulihkan situasi yang terasa identik secara visual bagi pengguna. Menang! Namun, jendela yang diubah ukurannya berarti setiap item berpotensi mengubah tingginya, jadi bagaimana kita tahu seberapa jauh konten yang ditambatkan harus ditempatkan? Kami tidak! Untuk mengetahuinya, kita harus menata setiap elemen di atas item yang ditambatkan dan menjumlahkan semua tingginya; hal ini dapat menyebabkan jeda yang signifikan setelah pengubahan ukuran, dan kita tidak menginginkannya. Sebagai gantinya, kita mengasumsikan bahwa setiap item di atas memiliki ukuran yang sama dengan penanda makam dan menyesuaikan posisi scroll kita. Saat elemen di-scroll ke dalam jalur, kami menyesuaikan posisi scroll, sehingga secara efektif menunda pekerjaan tata letak hingga benar-benar diperlukan.
Tata Letak
Saya telah melewatkan detail penting: Tata Letak. Setiap daur ulang elemen DOM biasanya akan menata ulang seluruh jalur yang akan membuat kita jauh di bawah target 60 frame per detik. Untuk menghindarinya, kami mengambil beban tata letak sendiri dan menggunakan elemen yang diposisikan secara mutlak dengan transformasi. Dengan begitu, kita dapat berpura-pura bahwa semua elemen yang lebih jauh di landasan masih memakan ruang, padahal sebenarnya hanya ada ruang kosong. Karena kita melakukan penataan sendiri, kita dapat menyimpan posisi akhir setiap item ke cache dan kita dapat segera memuat elemen yang benar dari cache saat pengguna men-scroll ke belakang.
Idealnya, item hanya akan dicat ulang satu kali saat dilampirkan ke DOM dan tidak terpengaruh oleh penambahan atau penghapusan item lain di jalur. Hal itu mungkin dilakukan, tetapi hanya dengan browser modern.
Penyesuaian mutakhir
Baru-baru ini, Chrome menambahkan dukungan untuk Pembatasan CSS, sebuah fitur
yang memungkinkan kami developer memberi tahu browser bahwa suatu elemen adalah batas untuk
tata letak dan pekerjaan menggambar. Karena kita melakukan tata letak sendiri di sini, ini adalah aplikasi utama untuk penampungan. Setiap kali menambahkan elemen ke jalur, kita tahu
bahwa item lain tidak perlu terpengaruh oleh tata ulang. Jadi, setiap item harus
mendapatkan contain: layout
. Kita juga tidak ingin memengaruhi bagian lain situs,
jadi runway itu sendiri juga harus mendapatkan direktif gaya ini.
Hal lain yang kami pertimbangkan adalah menggunakan
IntersectionObservers
sebagai mekanisme untuk mendeteksi kapan
pengguna telah men-scroll cukup jauh sehingga kami dapat mulai mendaur ulang elemen dan memuat
data baru. Namun, IntersectionObserver ditentukan memiliki latensi tinggi (seolah-olah menggunakan requestIdleCallback
), sehingga kita mungkin merasa kurang responsif dengan IntersectionObserver dibandingkan tanpa IntersectionObserver. Bahkan implementasi kami saat ini yang menggunakan
peristiwa scroll
mengalami masalah ini, karena peristiwa scroll dikirimkan berdasarkan
“upaya terbaik”. Pada akhirnya, Compositor Worklet Houdini
akan menjadi solusi fidelitas tinggi untuk masalah ini.
Masih belum sempurna
Implementasi DOM recycling saat ini tidak ideal karena menambahkan semua elemen yang lulus melalui area tampilan, bukan hanya yang benar-benar ada di layar. Artinya, saat Anda men-scroll sangat cepat, Anda memberikan banyak pekerjaan untuk tata letak dan menggambar di Chrome sehingga Chrome tidak dapat mengikutinya. Anda hanya akan melihat latar belakang. Ini bukan akhir dari segalanya, tetapi tentu saja ada yang perlu ditingkatkan.
Kami harap Anda memahami betapa sulitnya masalah sederhana ketika Anda ingin menggabungkan pengalaman pengguna yang luar biasa dengan standar performa yang tinggi. Dengan Progressive Web Apps menjadi pengalaman inti di ponsel, hal ini akan menjadi lebih penting dan developer web harus terus berinvestasi dalam menggunakan pola yang memperhatikan batasan performa.
Semua kode dapat ditemukan di repositori kami. Kami telah melakukan yang terbaik untuk menjaganya agar dapat digunakan kembali, tetapi tidak akan memublikasikannya sebagai library sebenarnya di npm atau sebagai repo terpisah. Penggunaan utamanya adalah untuk pendidikan.