Scrollspy-Navigation erstellen

Eine Scrollspy-Navigation ist ein gängiges UI-Muster, bei dem der Navigationslink, der dem aktuell im Darstellungsbereich angezeigten Abschnitt der Seite entspricht, automatisch hervorgehoben wird. So können Nutzer beim Scrollen durch ein langes Dokument leicht erkennen, wo sie sich gerade befinden.

Dieses Muster ist in zwei Szenarien üblich:

  • Websites mit nur einer Seite: Die primäre Website-Navigation verweist auf verschiedene Abschnitte einer einzelnen Seite und nicht auf separate URLs.
  • Lange Inhalte: Ein Inhaltsverzeichnis oder eine seitliche Navigationsleiste zeigt den Fortschritt des Nutzers in einem Artikel, einem Leitfaden oder einer Dokumentation an.

Implementierung

Früher waren für das Scrollspy-Muster JavaScript und die IntersectionObserver API erforderlich. Modernes CSS bietet eine native Möglichkeit, dies mit der Property scroll-target-group und der Pseudoklasse :target-current zu erreichen.

  1. Fragment-IDs für Navigationslinks verwenden: Ihre Navigation muss aus Ankerlinks (<a>) bestehen, die auf Abschnitte auf derselben Seite verweisen. Verwenden Sie dazu Fragment-IDs (z. B. <a href="#section-1">), die mit der id Ihrer Inhaltsabschnitte übereinstimmen.
  2. Scrollzielgruppierung aktivieren: Wende die Property scroll-target-group: auto auf den Container deiner Navigationslinks an. Dadurch wird der Browser angewiesen, diese Links als Markierungen zu behandeln, mit denen die jeweiligen Ziele erfasst werden.
  3. Aktiven Link gestalten: Verwenden Sie die Pseudoklasse :target-current für Ihre Navigationslinks, um Stile anzuwenden, wenn der Zielabschnitt im Scroll-Container sichtbar ist.
  4. Semantischer Status: Verwenden Sie ein Script, um aria-current="true" für den aktiven Link zu aktualisieren, wenn das Scrollen beendet wird. So wird der Status korrekt an Hilfstechnologien übermittelt.

Beispielcode

In diesem Beispiel wird der aktive Link in einem fixierten Navigationsmenü hervorgehoben, während der Nutzer durch die Abschnitte scrollt.


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

Best Practices

  • DO use scroll-target-group: auto on a dedicated container for navigation links: This ensures the browser knows which links should be tracked.
  • EMPFOHLEN: Achten Sie darauf, dass Ziele eindeutige id-Attribute haben, die mit den href-Attributen der Links übereinstimmen.
  • DO provide enough vertical space for sections: scroll-target-group most clearly identifies the visible target when sections are large enough that only one or two are visible at a time.
  • OBLIGATORISCH: Aus Gründen der Barrierefreiheit muss der visuelle Status mit aria-current gespiegelt werden. :target-current übernimmt zwar den visuellen Aspekt, wird aber nicht für Screenreader bereitgestellt.
  • AVOID Sie es, sich für den aktiven Status ausschließlich auf die Farbe zu verlassen: Verwenden Sie andere visuelle Hinweise wie Schriftstärke, Unterstreichung oder einen Indikatorpunkt oder sorgen Sie für ausreichenden Kontrast zwischen den Farben, damit der Unterschied für Nutzer mit Farbenfehlsichtigkeit erkennbar ist.

Fallback-Strategien

Für Browser, die scroll-target-group nicht unterstützen, sollten Sie ein Fallback auf Basis der IntersectionObserver API verwenden.

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