Bagaimana jika saya memberi tahu Anda bahwa ada lebih dari satu area pandang.
BRRRRAAAAAAAMMMMMMMMMM
Dan area pandang yang Anda gunakan saat ini, sebenarnya adalah area pandang dalam area pandang.
BRRRRAAAAAAAMMMMMMMMMM
Terkadang, data yang diberikan DOM kepada Anda merujuk ke salah satu area pandang tersebut, bukan yang lainnya.
BRRRRAAAAM… tunggu, apa?
Benar, lihat:
Area pandang tata letak vs. area pandang visual
Video di atas menunjukkan halaman web yang di-scroll dan diperbesar dengan mencubit, beserta peta mini di sebelah kanan yang menampilkan posisi area pandang dalam halaman.
Semuanya cukup mudah selama scroll biasa. Area hijau
mewakili tampilan area tata letak, yang dipatuhi item position: fixed
.
Hal-hal menjadi aneh saat fitur cubit untuk zoom diperkenalkan. Kotak merah mewakili
area pandang visual, yang merupakan bagian dari halaman yang benar-benar dapat kita lihat. Area pandang
ini dapat berpindah-pindah sementara elemen position: fixed
tetap berada
di tempatnya, yang terpasang ke area pandang tata letak. Jika kita menggeser pada batas area pandang
tata letak, area pandang tata letak akan ikut terbawa.
Meningkatkan kompatibilitas
Sayangnya, web API tidak konsisten dalam hal area pandang yang dirujuknya, dan juga tidak konsisten di seluruh browser.
Misalnya, element.getBoundingClientRect().y
menampilkan offset dalam
area tampilan tata letak. Itu bagus, tetapi kita sering kali menginginkan posisi dalam halaman,
jadi kita menulis:
element.getBoundingClientRect().y + window.scrollY
Namun, banyak browser menggunakan area pandang visual untuk window.scrollY
, yang berarti
kode di atas akan rusak saat pengguna melakukan zoom dengan mencubit.
Chrome 61 mengubah window.scrollY
untuk merujuk ke area pandang tata letak,
yang berarti kode di atas berfungsi bahkan saat di-pinch-zoom. Faktanya, browser
secara perlahan mengubah semua properti posisi untuk merujuk ke area pandang tata letak.
Dengan pengecualian satu properti baru…
Mengekspos area pandang visual ke skrip
API baru mengekspos area pandang visual sebagai window.visualViewport
. Ini adalah spesifikasi draf, dengan persetujuan lintas browser, dan
diluncurkan di Chrome 61.
console.log(window.visualViewport.width);
Berikut adalah hal yang diberikan window.visualViewport
kepada kita:
visualViewport properti |
|
---|---|
offsetLeft
|
Jarak antara tepi kiri area pandang visual, dan area pandang tata letak, dalam piksel CSS. |
offsetTop
|
Jarak antara tepi atas area pandang visual, dan area pandang tata letak, dalam piksel CSS. |
pageLeft
|
Jarak antara tepi kiri area pandang visual, dan batas kiri dokumen, dalam piksel CSS. |
pageTop
|
Jarak antara tepi atas area pandang visual, dan batas atas dokumen, dalam piksel CSS. |
width
|
Lebar area pandang visual dalam piksel CSS. |
height
|
Tinggi area pandang visual dalam piksel CSS. |
scale
|
Skala yang diterapkan dengan mencubit untuk zoom. Jika konten berukuran dua kali lipat karena
zoom, tindakan ini akan menampilkan 2 . Hal ini tidak terpengaruh oleh
devicePixelRatio .
|
Ada juga beberapa peristiwa:
window.visualViewport.addEventListener('resize', listener);
visualViewport peristiwa |
|
---|---|
resize
|
Diaktifkan saat width , height , atau
scale berubah.
|
scroll
|
Diaktifkan saat offsetLeft atau offsetTop berubah.
|
Demo
Video di awal artikel ini dibuat menggunakan visualViewport
,
lihat di Chrome 61+. Video ini menggunakan
visualViewport
untuk membuat peta mini tetap berada di kanan atas area pandang
visual, dan menerapkan skala terbalik sehingga selalu muncul dengan ukuran yang sama,
meskipun melakukan cubit untuk zoom.
Gotcha
Peristiwa hanya diaktifkan saat area pandang visual berubah
Hal ini terasa seperti hal yang jelas untuk dinyatakan, tetapi saya tidak menyadarinya saat pertama kali
bermain dengan visualViewport
.
Jika ukuran area pandang tata letak diubah, tetapi area pandang visual tidak diubah, Anda tidak akan mendapatkan
peristiwa resize
. Namun, viewport tata letak yang diubah ukurannya tanpa
viewport visual juga mengubah lebar/tinggi adalah hal yang tidak biasa.
Masalah sebenarnya adalah scroll. Jika scroll terjadi, tetapi area pandang visual
tetap statis secara relatif terhadap area pandang tata letak, Anda tidak akan mendapatkan peristiwa scroll
di visualViewport
, dan ini sangat umum. Selama scroll dokumen
reguler, area pandang visual tetap terkunci ke kiri atas area pandang
tata letak, sehingga scroll
tidak diaktifkan di visualViewport
.
Jika ingin mengetahui semua perubahan pada area pandang visual, termasuk
pageTop
dan pageLeft
, Anda juga harus memproses peristiwa scroll jendela:
visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
window.addEventListener('scroll', update);
Menghindari duplikasi pekerjaan dengan beberapa pemroses
Serupa dengan memproses scroll
& resize
di jendela, Anda kemungkinan akan memanggil
semacam fungsi "update" sebagai hasilnya. Namun, banyak peristiwa ini terjadi secara bersamaan. Jika pengguna mengubah ukuran jendela, tindakan ini akan
memicu resize
, tetapi sering kali juga memicu scroll
. Untuk meningkatkan performa, hindari
menangani perubahan beberapa kali:
// Add listeners
visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
addEventListener('scroll', update);
let pendingUpdate = false;
function update() {
// If we're already going to handle an update, return
if (pendingUpdate) return;
pendingUpdate = true;
// Use requestAnimationFrame so the update happens before next render
requestAnimationFrame(() => {
pendingUpdate = false;
// Handle update here
});
}
Saya telah mengajukan masalah spesifikasi untuk hal ini, karena saya pikir mungkin ada
cara yang lebih baik, seperti satu peristiwa update
.
Pengendali peristiwa tidak berfungsi
Karena bug Chrome, tindakan ini tidak berfungsi:
Bermasalah – menggunakan pengendali peristiwa
visualViewport.onscroll = () => console.log('scroll!');
Sebagai gantinya:
Berfungsi – menggunakan pemroses peristiwa
visualViewport.addEventListener('scroll', () => console.log('scroll'));
Nilai offset dibulatkan
Saya rasa (semoga) ini adalah bug Chrome lainnya.
offsetLeft
dan offsetTop
dibulatkan, yang cukup tidak akurat setelah
pengguna memperbesar. Anda dapat melihat masalah ini selama demo – jika pengguna melakukan zoom in dan menggeser
lahan secara perlahan, peta mini akan berpindah di antara piksel yang tidak di-zoom.
Kecepatan peristiwa lambat
Seperti peristiwa resize
dan scroll
lainnya, peristiwa ini tidak diaktifkan setiap frame,
terutama di perangkat seluler. Anda dapat melihatnya selama demo – setelah Anda melakukan zoom dengan mencubit, peta mini
akan mengalami masalah untuk tetap terkunci ke area pandang.
Aksesibilitas
Dalam demo, saya menggunakan visualViewport
untuk
mengatasi tindakan cubit-zoom pengguna. Hal ini masuk akal untuk demo khusus ini, tetapi
Anda harus berpikir dengan cermat sebelum melakukan apa pun yang mengganti keinginan
pengguna untuk memperbesar.
visualViewport
dapat digunakan untuk meningkatkan aksesibilitas. Misalnya, jika pengguna
memperbesar, Anda dapat memilih untuk menyembunyikan item position: fixed
dekoratif, agar
tidak mengganggu pengguna. Namun, sekali lagi, berhati-hatilah agar Anda tidak menyembunyikan sesuatu
yang ingin dilihat lebih dekat oleh pengguna.
Anda dapat mempertimbangkan untuk memposting ke layanan analisis saat pengguna memperbesar. Hal ini dapat membantu Anda mengidentifikasi halaman yang sulit diakses pengguna pada tingkat zoom default.
visualViewport.addEventListener('resize', () => {
if (visualViewport.scale > 1) {
// Post data to analytics service
}
});
Selesai. visualViewport
adalah API kecil yang bagus yang menyelesaikan masalah
kompatibilitas di sepanjang jalan.