Tworzenie nawigacji Scrollspy

Nawigacja scrollspy to popularny wzorzec interfejsu, który automatycznie wyróżnia link nawigacyjny odpowiadający sekcji strony aktualnie widocznej w obszarze widoku. Dzięki temu użytkownicy mogą łatwo sprawdzić, w którym miejscu długiego dokumentu się znajdują, gdy go przewijają.

Ten wzorzec jest typowy w 2 przypadkach:

  • Witryny z pojedynczą stroną: w których główna nawigacja prowadzi do różnych sekcji pojedynczej strony, a nie do osobnych adresów URL.
  • Długie treści: gdy spis treści lub pasek boczny śledzi postępy użytkownika w artykule, przewodniku lub dokumentacji.

Jak to zrobić?

Tradycyjnie wzorzec scrollspy wymagał JavaScriptu i interfejsu API IntersectionObserver. Nowoczesny CSS zapewnia natywny sposób osiągnięcia tego celu za pomocą właściwości scroll-target-group i pseudoklasy :target-current.

  1. Upewnij się, że linki nawigacyjne używają identyfikatorów fragmentów: nawigacja musi składać się z linków do kotwic (<a>), które wskazują sekcje na tej samej stronie za pomocą identyfikatorów fragmentów (np. <a href="#section-1">) odpowiadających atrybutowi id sekcji treści.
  2. Włącz grupowanie elementów docelowych przewijania: zastosuj właściwość scroll-target-group: auto do kontenera linków nawigacyjnych. Dzięki temu przeglądarka traktuje te linki jako znaczniki, które śledzą odpowiednie cele.
  3. Styl aktywnego linku: użyj pseudoklasy :target-current w linkach nawigacyjnych, aby zastosować style, gdy ich sekcja docelowa jest widoczna w kontenerze przewijania.
  4. Stan semantyczny: użyj skryptu, aby zaktualizować aria-current="true" w aktywnym linku, gdy przewijanie się zatrzyma, aby stan był prawidłowo przekazywany do technologii wspomagających osoby z niepełnosprawnością.

Przykładowy kod

W tym przykładzie aktywny link w stałym menu nawigacyjnym jest podświetlany, gdy użytkownik przewija sekcje.


/* 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>

Sprawdzone metody

  • UŻYWAJ atrybutu scroll-target-group: auto w dedykowanym kontenerze linków nawigacyjnych: dzięki temu przeglądarka będzie wiedzieć, które linki powinny być śledzone.
  • UPEWNIJ SIĘ, że cele mają unikalne atrybuty id pasujące do atrybutów href linków.
  • ZAPEWNIJ wystarczającą ilość miejsca w pionie na sekcje: scroll-target-group najdokładniej identyfikuje widoczny cel, gdy sekcje są na tyle duże, że jednocześnie widoczne są tylko 1 lub 2 z nich.
  • OBOWIĄZKOWE: ze względu na ułatwienia dostępu stan wizualny musi być odzwierciedlony za pomocą aria-current. :target-current obsługuje aspekt wizualny, ale nie jest on widoczny dla czytników ekranu.
  • AVOID polegania wyłącznie na kolorze w przypadku stanu aktywnego: dodaj inne wskazówki wizualne, takie jak grubość czcionki, podkreślenie lub kropka, albo upewnij się, że kontrast między kolorami jest wystarczający, aby różnica była widoczna dla użytkowników z zaburzeniami rozpoznawania barw.

Strategie zastępcze

W przypadku przeglądarek, które nie obsługują scroll-target-group, należy użyć kreacji zastępczej opartej na interfejsie API IntersectionObserver.

<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>