TL;DR
Berikut rahasianya: Anda mungkin tidak memerlukan peristiwa scroll
di aplikasi berikutnya. Dengan menggunakan
IntersectionObserver
,
saya akan menunjukkan cara memicu peristiwa kustom saat elemen position:sticky
menjadi tetap atau saat berhenti menempel. Semuanya tanpa
menggunakan pemroses scroll. Bahkan ada demo keren untuk membuktikannya:
Memperkenalkan peristiwa sticky-change
Salah satu batasan praktis penggunaan posisi melekat CSS adalah tidak memberikan sinyal platform untuk mengetahui kapan properti aktif. Dengan kata lain, tidak ada peristiwa untuk mengetahui kapan elemen menjadi melekat atau saat elemen berhenti melekat.
Ambil contoh berikut, yang memperbaiki Bukankah akan lebih baik jika browser memberi tahu saat elemen mencapai tanda tersebut?
Rupanya saya bukan satu-satunya
yang berpikir demikian. Sinyal untuk Dengan mempertimbangkan kasus penggunaan ini, kami telah membuat sasaran akhir: membuat peristiwa yang diaktifkan saat elemen Demo menggunakan
peristiwa ini untuk memberi header bayangan jatuh saat diperbaiki. Tindakan ini juga akan memperbarui judul baru di bagian atas halaman. Mari kita bahas beberapa terminologi agar saya dapat merujuk ke nama-nama ini
di seluruh postingan: Untuk mengetahui header mana yang memasuki "mode melekat", kita memerlukan beberapa cara untuk menentukan
offset scroll penampung scroll. Hal ini akan memberi kita cara
untuk menghitung header yang saat ini ditampilkan. Namun, hal itu cukup
sulit dilakukan tanpa peristiwa Jadi, tanpa peristiwa scroll, kita kehilangan kemampuan untuk melakukan penghitungan terkait
tata letak pada header. Daripada peristiwa Kita memerlukan dua sentinel untuk mencakup empat kasus scroll ke atas dan ke bawah: Sebaiknya lihat screencast 1-4 dalam urutan terjadinya: Sentinel diposisikan di bagian atas dan bawah setiap bagian.
Intersection Observer mengamati perubahan secara asinkron pada persimpangan elemen target dan area pandang dokumen atau penampung induk. Dalam kasus ini,
kita mengamati persimpangan dengan penampung induk.
Sos ajaibnya adalah Pertama, saya menyiapkan observer untuk sentinel header dan footer: Kemudian, saya menambahkan observer untuk diaktifkan saat elemen Pengamat dikonfigurasi dengan Prosesnya serupa untuk sentinel bawah ( Pengamat dikonfigurasi dengan Terakhir, ada dua utilitas saya untuk mengaktifkan peristiwa kustom Selesai. Kami membuat peristiwa kustom saat elemen dengan Saya sering bertanya-tanya apakah Tidak juga. Yang kita butuhkan adalah cara untuk mengamati perubahan gaya pada elemen DOM.
Sayangnya, tidak ada di API platform web yang memungkinkan Anda
melihat perubahan gaya. Di masa mendatang, ekstensi "Style Mutation Observer" ke Mutation Observer mungkin berguna untuk mengamati perubahan pada gaya yang dihitung elemen.
Kecuali dinyatakan lain, konten di halaman ini dilisensikan berdasarkan Lisensi Creative Commons Attribution 4.0, sedangkan contoh kode dilisensikan berdasarkan Lisensi Apache 2.0. Untuk mengetahui informasi selengkapnya, lihat Kebijakan Situs Google Developers. Java adalah merek dagang terdaftar dari Oracle dan/atau afiliasinya. Terakhir diperbarui pada 2017-09-19 UTC..sticky {
position: sticky;
top: 10px;
}
position:sticky
dapat membuka sejumlah kasus penggunaan:
position:sticky
diperbaiki. Mari kita sebut peristiwa ini sebagai
peristiwa sticky-change
:document.addEventListener('sticky-change', e => {
const header = e.detail.target; // header became sticky or stopped sticking.
const sticking = e.detail.stuck; // true when header is sticky.
header.classList.toggle('shadow', sticking); // add drop shadow when sticking.
document.querySelector('.who-is-sticking').textContent = header.textContent;
});
Efek scroll tanpa peristiwa scroll?
position:sticky
.position:sticky
diterapkan ke elemen.scroll
:) Masalah lainnya adalah
position:sticky
menghapus elemen dari tata letak saat menjadi tetap.Menambahkan DOM dummy untuk menentukan posisi scroll
scroll
, kita akan menggunakan IntersectionObserver
untuk
menentukan kapan header masuk dan keluar dari mode melekat. Menambahkan dua node
(alias sentinel) di setiap bagian melekat, satu di bagian atas dan satu
di bagian bawah, akan berfungsi sebagai titik jalan untuk mengetahui posisi scroll. Saat penanda ini
memasuki dan keluar dari penampung, visibilitasnya berubah dan
Intersection Observer akan memicu callback.
CSS
.sticky_sentinel--top
berada di bagian atas header, sedangkan
.sticky_sentinel--bottom
berada di bagian bawah bagian:
:root {
--default-padding: 16px;
--header-height: 80px;
}
.sticky {
position: sticky;
top: 10px; /* adjust sentinel height/positioning based on this position. */
height: var(--header-height);
padding: 0 var(--default-padding);
}
.sticky_sentinel {
position: absolute;
left: 0;
right: 0; /* needs dimensions */
visibility: hidden;
}
.sticky_sentinel--top {
/* Adjust the height and top values based on your on your sticky top position.
e.g. make the height bigger and adjust the top so observeHeaders()'s
IntersectionObserver fires as soon as the bottom of the sentinel crosses the
top of the intersection container. */
height: 40px;
top: -24px;
}
.sticky_sentinel--bottom {
/* Height should match the top of the header when it's at the bottom of the
intersection container. */
height: calc(var(--header-height) + var(--default-padding));
bottom: 0;
}
Menyiapkan Intersection Observer
IntersectionObserver
. Setiap sentinel mendapatkan
IntersectionObserver
untuk mengamati visibilitas interseksi dalam
penampung scroll. Saat sentinel men-scroll ke area pandang yang terlihat, kita tahu bahwa header menjadi tetap atau berhenti melekat. Demikian pula, saat sentinel keluar dari area pandang./**
* Notifies when elements w/ the `sticky` class begin to stick or stop sticking.
* Note: the elements should be children of `container`.
* @param {!Element} container
*/
function observeStickyHeaderChanges(container) {
observeHeaders(container);
observeFooters(container);
}
observeStickyHeaderChanges(document.querySelector('#scroll-container'));
.sticky_sentinel--top
melewati
bagian atas penampung scroll (ke arah mana pun).
Fungsi observeHeaders
membuat sentinel teratas dan menambahkannya ke
setiap bagian. Observer menghitung persimpangan sentinel dengan
bagian atas penampung dan memutuskan apakah sentinel tersebut memasuki atau keluar dari area pandang. Informasi
tersebut menentukan apakah header bagian tetap atau tidak./**
* Sets up an intersection observer to notify when elements with the class
* `.sticky_sentinel--top` become visible/invisible at the top of the container.
* @param {!Element} container
*/
function observeHeaders(container) {
const observer = new IntersectionObserver((records, observer) => {
for (const record of records) {
const targetInfo = record.boundingClientRect;
const stickyTarget = record.target.parentElement.querySelector('.sticky');
const rootBoundsInfo = record.rootBounds;
// Started sticking.
if (targetInfo.bottom < rootBoundsInfo.top) {
fireEvent(true, stickyTarget);
}
// Stopped sticking.
if (targetInfo.bottom >= rootBoundsInfo.top &&
targetInfo.bottom < rootBoundsInfo.bottom) {
fireEvent(false, stickyTarget);
}
}
}, {threshold: [0], root: container});
// Add the top sentinels to each section and attach an observer.
const sentinels = addSentinels(container, 'sticky_sentinel--top');
sentinels.forEach(el => observer.observe(el));
}
threshold: [0]
sehingga callback-nya diaktifkan segera
setelah sentinel terlihat..sticky_sentinel--bottom
).
Pengamat kedua dibuat untuk diaktifkan saat footer melewati bagian bawah
penampung scroll. Fungsi observeFooters
membuat
node sentinel dan melampirkannya ke setiap bagian. Observer menghitung
titik temu sentinel dengan bagian bawah penampung dan memutuskan apakah
sentinel tersebut masuk atau keluar. Informasi tersebut menentukan apakah header bagian
tetap atau tidak./**
* Sets up an intersection observer to notify when elements with the class
* `.sticky_sentinel--bottom` become visible/invisible at the bottom of the
* container.
* @param {!Element} container
*/
function observeFooters(container) {
const observer = new IntersectionObserver((records, observer) => {
for (const record of records) {
const targetInfo = record.boundingClientRect;
const stickyTarget = record.target.parentElement.querySelector('.sticky');
const rootBoundsInfo = record.rootBounds;
const ratio = record.intersectionRatio;
// Started sticking.
if (targetInfo.bottom > rootBoundsInfo.top && ratio === 1) {
fireEvent(true, stickyTarget);
}
// Stopped sticking.
if (targetInfo.top < rootBoundsInfo.top &&
targetInfo.bottom < rootBoundsInfo.bottom) {
fireEvent(false, stickyTarget);
}
}
}, {threshold: [1], root: container});
// Add the bottom sentinels to each section and attach an observer.
const sentinels = addSentinels(container, 'sticky_sentinel--bottom');
sentinels.forEach(el => observer.observe(el));
}
threshold: [1]
sehingga callback-nya diaktifkan saat
seluruh node berada dalam tampilan.sticky-change
dan membuat sentinel:/**
* @param {!Element} container
* @param {string} className
*/
function addSentinels(container, className) {
return Array.from(container.querySelectorAll('.sticky')).map(el => {
const sentinel = document.createElement('div');
sentinel.classList.add('sticky_sentinel', className);
return el.parentElement.appendChild(sentinel);
});
}
/**
* Dispatches the `sticky-event` custom event on the target element.
* @param {boolean} stuck True if `target` is sticky.
* @param {!Element} target Element to fire the event on.
*/
function fireEvent(stuck, target) {
const e = new CustomEvent('sticky-change', {detail: {stuck, target}});
document.dispatchEvent(e);
}
Demo akhir
position:sticky
menjadi
tetap dan menambahkan efek scroll tanpa menggunakan peristiwa scroll
.Kesimpulan
IntersectionObserver
akan
menjadi alat yang berguna untuk menggantikan beberapa pola UI berbasis peristiwa scroll
yang
telah dikembangkan selama bertahun-tahun. Ternyata jawabannya adalah ya dan tidak. Semantik
IntersectionObserver
API membuatnya sulit digunakan untuk semuanya. Namun, seperti
yang telah saya tunjukkan di sini, Anda dapat menggunakannya untuk beberapa teknik yang menarik.Cara lain untuk mendeteksi perubahan gaya?
MutationObserver
akan menjadi pilihan pertama yang logis, tetapi tidak berfungsi untuk
sebagian besar kasus. Misalnya, dalam demo, kita akan menerima callback saat class sticky
ditambahkan ke elemen, tetapi tidak saat gaya yang dihitung elemen berubah.
Ingat bahwa class sticky
sudah dideklarasikan saat halaman dimuat.position: sticky
.