Membuat Navigasi Scrollspy

Navigasi scrollspy adalah pola UI umum yang otomatis menandai link navigasi yang sesuai dengan bagian halaman yang saat ini berada di area pandang. Hal ini memberikan gambaran yang jelas kepada pengguna tentang posisi mereka dalam dokumen panjang saat men-scroll.

Pola ini umum terjadi dalam dua skenario:

  • Situs satu halaman: Tempat navigasi situs utama menautkan ke berbagai bagian dari satu halaman, bukan URL terpisah.
  • Konten panjang: Daftar isi atau navigasi sidebar melacak progres pengguna dalam membaca artikel, panduan, atau dokumentasi.

Cara menerapkan

Biasanya, pola scrollspy memerlukan JavaScript dan IntersectionObserver API. CSS modern menyediakan cara native untuk mencapai hal ini menggunakan properti scroll-target-group dan pseudo-class :target-current.

  1. Pastikan Link Navigasi menggunakan ID Fragmen: Navigasi Anda harus terdiri dari link anchor (<a>) yang mengarah ke bagian di halaman yang sama menggunakan ID fragmen (misalnya, <a href="#section-1">) yang cocok dengan id bagian konten Anda.
  2. Aktifkan Pengelompokan Target Scroll: Terapkan properti scroll-target-group: auto ke penampung link navigasi Anda. Tindakan ini akan memberi tahu browser untuk memperlakukan link ini sebagai penanda yang melacak target masing-masing.
  3. Menata Gaya Link Aktif: Gunakan pseudo-class :target-current pada link navigasi Anda untuk menerapkan gaya saat bagian targetnya terlihat di penampung scroll.
  4. Status Semantik: Gunakan skrip untuk memperbarui aria-current="true" di link aktif saat scroll berhenti untuk memastikan status dikomunikasikan dengan benar ke teknologi pendukung.

Kode contoh

Contoh ini menandai link aktif di menu navigasi tetap saat pengguna men-scroll bagian.


/* MANDATORY: The container of markers must have scroll-target-group: auto */
nav {
  /* Enables automatic tracking of fragment links */
  scroll-target-group: auto;
}
/* MANDATORY: Use :target-current to style the link whose target is in view. */
nav a:target-current {
  color: var(--accent, #007bff);
  border-bottom-color: var(--accent, #007bff);
}
<nav>
  <a href="#intro">Introduction</a>
  <a href="#features">Features</a>
  <a href="#pricing">Pricing</a>
</nav>

<main>
  <!-- MANDATORY: Each section must have a unique ID matching a link fragment in the <nav>. -->
  <section id="intro">
    <h2>Introduction</h2>
    <!-- content -->
  </section>
  <section id="features">
    <h2>Features</h2>
    <!-- content -->
  </section>
  <section id="pricing">
    <h2>Pricing</h2>
    <!-- content -->
  </section>
</main>

<script type="module">
  if (CSS.supports('scroll-target-group: auto')) {
    const syncAriaCurrent = () => {
      const currentLink = document.querySelector('nav a:target-current');
      document.querySelectorAll('nav a').forEach(link => {
        link.setAttribute('aria-current', link === currentLink ? 'true' : 'false');
      });
    };
    // Update the aria on initial page load. Ensure the DOM content is loaded, either by using `<script type="module">` or an event listener for `DOMContentLoaded`.
    syncAriaCurrent();
    // Update the aria after scroll ends
    document.addEventListener('scrollend', syncAriaCurrent);
  }
</script>

Praktik Terbaik

  • LAKUKAN penggunaan scroll-target-group: auto pada penampung khusus untuk link navigasi: Hal ini memastikan browser mengetahui link mana yang harus dilacak.
  • LAKUKAN: pastikan target memiliki atribut id unik yang cocok dengan atribut href link.
  • LAKUKAN: sediakan ruang vertikal yang cukup untuk bagian: scroll-target-group mengidentifikasi target yang terlihat dengan paling jelas saat bagian cukup besar sehingga hanya satu atau dua bagian yang terlihat sekaligus.
  • WAJIB: Untuk aksesibilitas, status visual harus dicerminkan dengan aria-current. Meskipun :target-current menangani aspek visual, hal ini tidak ditampilkan kepada pembaca layar.
  • AVOID mengandalkan warna saja untuk status aktif: Sertakan isyarat visual lain seperti ketebalan font, garis bawah, atau titik indikator, atau pastikan ada kontras yang memadai antara warna untuk memastikan perbedaannya dapat diakses oleh pengguna dengan gangguan penglihatan warna.

Strategi penggantian

Untuk browser yang tidak mendukung scroll-target-group, Anda harus menggunakan penggantian berdasarkan IntersectionObserver API.

<script>
  // Feature detection for scroll-target-group
  const supportsScrollTargetGroup = CSS.supports('scroll-target-group: auto');

  if (!supportsScrollTargetGroup) {
    const navLinks = document.querySelectorAll('nav a');
    const sections = document.querySelectorAll('main section');

    const observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          const id = entry.target.getAttribute('id');
          navLinks.forEach(link => {
            const isActive = link.getAttribute('href') === `#${id}`;
            // Use the escaped colon class for consistent styling
            link.classList.toggle('\:target-current', isActive);
            link.setAttribute('aria-current', isActive);
          });
        }
      });
    }, { rootMargin: '-50% 0px -50% 0px', threshold: 0 });

    sections.forEach(section => observer.observe(section));
  }
</script>

<style>
  /* Fallback style if :target-current is not supported.
     Use :is() to combine the native pseudo-class with a fallback class for older browsers. DO NOT use `nav a:target-current, nav a.\:target-current, as the unrecognized pseudoselector invalidates the entire rule in older browsers. */
  nav a:is(:target-current, .\:target-current) {
    color: var(--accent, #007bff);
    border-bottom-color: var(--accent, #007bff);
    font-weight: bold;
  }
</style>