Sinkronisasi Status Pengepasan Posisi Scroll

Menyinkronkan status UI dengan posisi snap penampung yang dapat di-scroll biasanya memerlukan pemroses peristiwa scroll yang kompleks, penghitungan manual offset scroll, dan observer persimpangan. Peristiwa scrollsnapchange memberikan cara native dan efisien untuk mendeteksi kapan scroller telah menetap di target snap baru, sehingga berguna untuk menyinkronkan sidebar atau menandai bagian aktif dalam daftar isi.

Penerapan

1. Mengonfigurasi Pengepasan Posisi Scroll di CSS

Penampung harus memiliki scroll-snap-type yang ditentukan, dan memiliki turunan dengan scroll-snap-align agar browser dapat melacak target snap. Dalam artikel panjang dengan daftar isi, Anda dapat menggunakannya untuk menempatkan header bagian ke bagian atas area pandang.

main {
    /* Enable scroll snapping on the container */  
  scroll-snap-type: y proximity;
  overflow-y: auto;
}

h2 {
  /* Define how headers align when snapped */
  scroll-snap-align: start;
}

2. Memproses Perubahan Snap

Gunakan peristiwa scrollsnapchange pada penampung scroll untuk bereaksi saat pengguna selesai men-scroll dan browser berpindah ke elemen baru. Dalam demo TOC, kami menggunakannya untuk menandai link aktif di sidebar.

<!-- MANDATORY: Wrap table of contents links inside a proper navigation landmark -->
<nav aria-label="Table of contents">
  <ul>
    <li><a href="#section-1" aria-current="location">Section 1</a></li>
    <li><a href="#section-2">Section 2</a></li>
  </ul>
</nav>
const main = document.getElementById('main');
const links = document.querySelectorAll('nav a');

// The event fires when the scroller settles on a new snap target
main.addEventListener('scrollsnapchange', (event) => {
  // Use snapTargetBlock for vertical or snapTargetInline for horizontal
  const snappedHeader = event.snapTargetBlock;
  
  if (snappedHeader) {
    setSelectedParagraph(snappedHeader.id);
  }
});

Aksesibilitas

Untuk daftar isi, pastikan link sidebar menggunakan aria-current="true" atau aria-current="location" saat aktif.

Selain itu, berhati-hatilah saat menggunakan nilai mandatory untuk scroll-snap-type, karena dapat menyebabkan konten di antara titik snap menjadi tidak dapat diakses jika lebih panjang dari layar.

Strategi penggantian

Jika scrollsnapchange tidak didukung, gunakan IntersectionObserver untuk mendeteksi elemen mana yang saat ini berada di bagian atas scroller. Perhatikan bahwa perilaku ini berbeda dengan scrollsnapchange, karena akan dipicu saat scroll terjadi, bukan hanya saat scroll telah selesai.

// Feature detect support for scroll snap events
if (!('onscrollsnapchange' in HTMLElement.prototype)) {
  const observer = new IntersectionObserver(
    () => {
      // Each time the set of intersecting headers changes, find the top
      // header that is visible.
      const topEntry = [...headers].reduce((currentTop, header) => {
        // Use the bottom to handle scrolling up, when the top is still offscreen
        const {bottom} = header.getBoundingClientRect();
        // Don't match if the header's bottom is above the scrollport
        if (bottom < 0) return;
        if (!currentTop) return header;
        return bottom <
          currentTop.getBoundingClientRect().bottom
          ? header
          : currentTop;
      }, undefined);
      if (topEntry) setSelectedParagraph(topEntry.id);
    },
    { root: main, threshold: 0.9 // Adjust based on your use case },
  );

  // Observe all snap targets (e.g., section headers)
  document.querySelectorAll('h2').forEach(header => observer.observe(header));
}