Scrollspy

In Bootstrap v6.0.0 neu aufgebaut

Navigation, List Group oder einfache Links markieren automatisch den Abschnitt, der gerade im sichtbaren Bereich liegt.

Scrollspy setzt die Klasse .active an Links (<a>), sobald das Element mit der passenden id aus dem href ins Bild scrollt. Am besten funktioniert das mit einer Nav oder List Group, aber auch beliebige Anker auf der Seite werden unterstützt.

  • Zwei Zutaten: eine Navigation (oder Links) und ein scrollbarer Container – entweder <body> oder ein Element mit fester height und overflow-y: scroll.
  • Aktivieren: Am Container data-bs-spy="scroll" und data-bs-target="#navId" setzen. Enthält er nichts Fokussierbares, zusätzlich tabindex="0" für Tastaturzugriff.
  • Ziele müssen existieren:<a href="#home"> braucht ein passendes Element wie <div id="home">, sonst wird der Link ignoriert.
  • Unsichtbare Ziele werden übersprungen (siehe Nicht sichtbare Elemente).
  • Sonderzeichen in IDs: Ziele werden per getElementById gesucht, daher funktionieren auch IDs wie #item.1, #section:overview oder #%2Fpath.

Quelle: Bootstrap-Dokumentation (englisch), übersetzt und bearbeitet. Doku lizenziert unter CC BY 3.0.

Erkennung

Scrollspy verfolgt die Abschnitte mit einem IntersectionObserver statt die Scrollposition laufend abzufragen. Maßgeblich ist allein eine Aktivierungslinie nahe der Oberkante des Scroll-Containers – das Verhalten ist dadurch vorhersehbar.

  • Aktiv ist der tiefste Abschnitt, dessen Oberkante die Aktivierungslinie bereits überschritten hat – also der, den du gerade liest.
  • Zwischen zwei Überschriften bleibt der zuletzt aktivierte Abschnitt aktiv, bis der nächste die Linie kreuzt.
  • Ganz oben, bevor ein Abschnitt die Linie erreicht, ist der erste Abschnitt aktiv.
  • Ganz unten wird der letzte Abschnitt aktiviert, auch wenn seine Oberkante die Linie nie erreicht – praktisch bei kurzen Schlussabschnitten.

Die Lage der Linie legst du mit topMargin fest (Prozent oder Pixel ab Oberkante, z. B. 96px unter einer Sticky-Navbar). Volle Kontrolle bietet der rohe IntersectionObserver-String rootMargin. Details unter Optionen.

Beispiele

Hinweis: Der Scroll-Container braucht eine feste Höhe und overflow. In der Originaldoku liefert das die Doku-eigene Klasse .scrollspy-example; hier ist es per Inline-Style style="height:200px;overflow:auto;position:relative;" ergänzt.

Navbar

Scrolle den Bereich unter der Navbar und beobachte, wie die aktive Klasse wechselt. Auch die Einträge im aufgeklappten Menü werden hervorgehoben.

Erste Überschrift

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Zweite Überschrift

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Dritte Überschrift

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Vierte Überschrift

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Fünfte Überschrift

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

<nav id="navbar-example2" class="navbar bg-1 px-3 mb-3 rounded-2">
  <a class="navbar-brand" href="#">Navbar</a>
  <ul class="nav nav-pills">
    <li class="nav-item">
      <a class="nav-link" href="#scrollspyHeading1">Erster</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="#scrollspyHeading2">Zweiter</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="#" role="button" data-bs-toggle="menu" aria-expanded="false">Menü</a>
      <div class="menu">
        <a class="menu-item" href="#scrollspyHeading3">Dritter</a>
        <a class="menu-item" href="#scrollspyHeading4">Vierter</a>
        <hr class="menu-divider">
        <a class="menu-item" href="#scrollspyHeading5">Fünfter</a>
      </div>
    </li>
  </ul>
</nav>
<div class="scrollspy-example bg-1 p-3 rounded-2" data-bs-spy="scroll" data-bs-target="#navbar-example2" data-bs-root-margin="0px 0px -40%" data-bs-smooth-scroll="true" tabindex="0" style="height:200px;overflow:auto;position:relative;">
  <h4 id="scrollspyHeading1">Erste Überschrift</h4>
  <p>...</p>
  <h4 id="scrollspyHeading2">Zweite Überschrift</h4>
  <p>...</p>
  <h4 id="scrollspyHeading3">Dritte Überschrift</h4>
  <p>...</p>
  <h4 id="scrollspyHeading4">Vierte Überschrift</h4>
  <p>...</p>
  <h4 id="scrollspyHeading5">Fünfte Überschrift</h4>
  <p>...</p>
</div>

Verschachtelte Nav

Scrollspy funktioniert auch mit verschachtelten .navs. Ist ein untergeordneter Eintrag aktiv, werden auch seine Eltern als aktiv markiert.

Eintrag 1

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Das JavaScript-Plugin versucht, unter allen sichtbaren Elementen das richtige auszuwählen. Mehrere gleichzeitig sichtbare Scrollspy-Ziele können zu Problemen führen.

Eintrag 1-1

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Das JavaScript-Plugin versucht, unter allen sichtbaren Elementen das richtige auszuwählen. Mehrere gleichzeitig sichtbare Scrollspy-Ziele können zu Problemen führen.

Eintrag 1-2

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Das JavaScript-Plugin versucht, unter allen sichtbaren Elementen das richtige auszuwählen. Mehrere gleichzeitig sichtbare Scrollspy-Ziele können zu Problemen führen.

Eintrag 2

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Das JavaScript-Plugin versucht, unter allen sichtbaren Elementen das richtige auszuwählen. Mehrere gleichzeitig sichtbare Scrollspy-Ziele können zu Problemen führen.

Eintrag 3

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Das JavaScript-Plugin versucht, unter allen sichtbaren Elementen das richtige auszuwählen. Mehrere gleichzeitig sichtbare Scrollspy-Ziele können zu Problemen führen.

Eintrag 3-1

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Das JavaScript-Plugin versucht, unter allen sichtbaren Elementen das richtige auszuwählen. Mehrere gleichzeitig sichtbare Scrollspy-Ziele können zu Problemen führen.

Eintrag 3-2

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Das JavaScript-Plugin versucht, unter allen sichtbaren Elementen das richtige auszuwählen. Mehrere gleichzeitig sichtbare Scrollspy-Ziele können zu Problemen führen.

<div class="row">
  <div class="col-4">
    <nav id="navbar-example3" class="h-100 flex-column align-items-stretch pe-4 border-end">
      <nav class="nav nav-pills flex-column">
        <a class="nav-link" href="#item-1">Eintrag 1</a>
        <nav class="nav nav-pills flex-column">
          <a class="nav-link ms-3 my-1" href="#item-1-1">Eintrag 1-1</a>
          <a class="nav-link ms-3 my-1" href="#item-1-2">Eintrag 1-2</a>
        </nav>
        <a class="nav-link" href="#item-2">Eintrag 2</a>
        <a class="nav-link" href="#item-3">Eintrag 3</a>
        <nav class="nav nav-pills flex-column">
          <a class="nav-link ms-3 my-1" href="#item-3-1">Eintrag 3-1</a>
          <a class="nav-link ms-3 my-1" href="#item-3-2">Eintrag 3-2</a>
        </nav>
      </nav>
    </nav>
  </div>
  <div class="col-8">
    <div data-bs-spy="scroll" data-bs-target="#navbar-example3" data-bs-smooth-scroll="true" class="scrollspy-example-2" tabindex="0" style="height:200px;overflow:auto;position:relative;">
      <div id="item-1">
        <h4>Eintrag 1</h4>
        <p>...</p>
      </div>
      <div id="item-1-1">
        <h5>Eintrag 1-1</h5>
        <p>...</p>
      </div>
      <div id="item-1-2">
        <h5>Eintrag 1-2</h5>
        <p>...</p>
      </div>
      <div id="item-2">
        <h4>Eintrag 2</h4>
        <p>...</p>
      </div>
      <div id="item-3">
        <h4>Eintrag 3</h4>
        <p>...</p>
      </div>
      <div id="item-3-1">
        <h5>Eintrag 3-1</h5>
        <p>...</p>
      </div>
      <div id="item-3-2">
        <h5>Eintrag 3-2</h5>
        <p>...</p>
      </div>
    </div>
  </div>
</div>

List Group

Auch mit .list-group klappt es. Scrolle den Bereich neben der Liste und beobachte den Wechsel der aktiven Klasse.

Eintrag 1

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Eintrag 2

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Eintrag 3

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Eintrag 4

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

<div class="row">
  <div class="col-4">
    <div id="list-example" class="list-group">
      <a class="list-group-item list-group-item-action" href="#list-item-1">Eintrag 1</a>
      <a class="list-group-item list-group-item-action" href="#list-item-2">Eintrag 2</a>
      <a class="list-group-item list-group-item-action" href="#list-item-3">Eintrag 3</a>
      <a class="list-group-item list-group-item-action" href="#list-item-4">Eintrag 4</a>
    </div>
  </div>
  <div class="col-8">
    <div data-bs-spy="scroll" data-bs-target="#list-example" data-bs-smooth-scroll="true" class="scrollspy-example" tabindex="0" style="height:200px;overflow:auto;position:relative;">
      <h4 id="list-item-1">Eintrag 1</h4>
      <p>...</p>
      <h4 id="list-item-2">Eintrag 2</h4>
      <p>...</p>
      <h4 id="list-item-3">Eintrag 3</h4>
      <p>...</p>
      <h4 id="list-item-4">Eintrag 4</h4>
      <p>...</p>
    </div>
  </div>
</div>

Einfache Anker

Scrollspy ist nicht auf Navs und List Groups beschränkt, sondern funktioniert mit allen <a>-Ankern im Dokument.

Eintrag 1

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Eintrag 2

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Eintrag 3

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Eintrag 4

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

Eintrag 5

Dies ist Platzhaltertext für die Scrollspy-Seite. Beim Scrollen wird jeweils der passende Navigationslink hervorgehoben. Der Text wiederholt sich im gesamten Beispiel, damit genug Inhalt zum Scrollen und Hervorheben vorhanden ist.

<div class="row">
  <div class="col-4">
    <div id="simple-list-example" class="d-flex flex-column gap-2 simple-list-example-scrollspy text-center">
      <a class="p-1 rounded" href="#simple-list-item-1">Eintrag 1</a>
      <a class="p-1 rounded" href="#simple-list-item-2">Eintrag 2</a>
      <a class="p-1 rounded" href="#simple-list-item-3">Eintrag 3</a>
      <a class="p-1 rounded" href="#simple-list-item-4">Eintrag 4</a>
      <a class="p-1 rounded" href="#simple-list-item-5">Eintrag 5</a>
    </div>
  </div>
  <div class="col-8">
    <div data-bs-spy="scroll" data-bs-target="#simple-list-example" data-bs-smooth-scroll="true" class="scrollspy-example" tabindex="0" style="height:200px;overflow:auto;position:relative;">
      <h4 id="simple-list-item-1">Eintrag 1</h4>
      <p>...</p>
      <h4 id="simple-list-item-2">Eintrag 2</h4>
      <p>...</p>
      <h4 id="simple-list-item-3">Eintrag 3</h4>
      <p>...</p>
      <h4 id="simple-list-item-4">Eintrag 4</h4>
      <p>...</p>
      <h4 id="simple-list-item-5">Eintrag 5</h4>
      <p>...</p>
    </div>
  </div>
</div>

Debug-Overlay

Wo genau Scrollspy einen Abschnitt als aktiv einstuft, ist nicht immer leicht vorstellbar. Die Originaldoku nutzt Scrollspy für ihr Inhaltsverzeichnis und bietet dort ein zuschaltbares Debug-Overlay mit roten Hilfslinien:

  • Die rot gestrichelte Linie oben ist die Aktivierungslinie. Ein Abschnitt wird aktiv, sobald seine Überschrift darüber scrollt. In der Doku liegt sie bei 96px, knapp unter der Navbar.
  • Die rote Zone unten leuchtet am Seitenende auf. Dort bleibt der letzte Abschnitt aktiv, auch wenn seine Überschrift die Linie nie erreicht.

Die Hilfslinien werden mit derselben topMargin/rootMargin-Berechnung erzeugt wie im Plugin (siehe Erkennung). Das Overlay selbst ist ein Werkzeug der Originaldoku und hier nicht eingebunden.

Nicht sichtbare Elemente

Unsichtbare Ziel-Elemente werden ignoriert, ihre Nav-Einträge erhalten also kein .active. Wird Scrollspy in einem unsichtbaren Wrapper initialisiert, ignoriert es alle Ziele. Rufe daher refresh auf, sobald der Wrapper sichtbar wird – etwa beim Wechsel eines Tabs:

document.querySelectorAll('#nav-tab>[data-bs-toggle="tab"]').forEach(el => {
  el.addEventListener('shown.bs.tab', () => {
    const target = el.getAttribute('data-bs-target')
    const scrollElem = document.querySelector(`${target} [data-bs-spy="scroll"]`)
    bootstrap.ScrollSpy.getOrCreateInstance(scrollElem).refresh()
  })
})

Verwendung

Per Data-Attribute

Setze data-bs-spy="scroll" an das zu beobachtende Element – meist <body> – und ergänze data-bs-target mit der id oder Klasse des Elternelements deiner .nav.

AttributBeschreibung
data-bs-spy="scroll"Aktiviert Scrollspy am scrollbaren Element (oft <body>).
data-bs-targetCSS-Selektor der Nav, deren Links zu den Abschnitten passen.
<body data-bs-spy="scroll" data-bs-target="#navbar-example">
  ...
  <div id="navbar-example">
    <ul class="nav nav-tabs" role="tablist">
      ...
    </ul>
  </div>
  ...
</body>

Per JavaScript

const scrollSpy = new bootstrap.ScrollSpy(document.body, {
  target: '#navbar-example'
})

Optionen

Optionen lassen sich per Data-Attribut oder JavaScript übergeben.

NameTypStandardBeschreibung
rootMarginstring, nullnullErweiterte Übersteuerung für das rootMargin des IntersectionObservers, z. B. 0px 0px -40%. Hat Vorrang vor topMargin. Bei null wird die Aktivierungslinie aus topMargin berechnet.
smoothScrollbooleanfalseSanftes Scrollen beim Klick auf einen Link zu einem beobachteten Abschnitt. Danach werden URL-Hash und Fokus auf den Abschnitt gesetzt.
targetstring, DOM-ElementnullElement, auf das Scrollspy angewendet wird.
thresholdarray[0]Gültiger threshold-Wert für den IntersectionObserver.
topMarginstring12%Lage der Aktivierungslinie ab Oberkante des Scroll-Containers, in Prozent (12%) oder Pixeln (96px, z. B. unter einer Sticky-Navbar). Wird ignoriert, wenn rootMargin gesetzt ist.

Methoden

MethodeBeschreibung
disposeEntfernt Scrollspy vom Element inklusive gespeicherter Daten.
getInstanceStatisch – liefert die Scrollspy-Instanz zu einem DOM-Element.
getOrCreateInstanceStatisch – liefert die Instanz oder erzeugt eine neue, falls noch keine existiert.
refreshNach dem Hinzufügen oder Entfernen von Elementen im DOM aufrufen.

Beispiel für refresh:

const dataSpyList = document.querySelectorAll('[data-bs-spy="scroll"]')
dataSpyList.forEach(dataSpyEl => {
  bootstrap.ScrollSpy.getInstance(dataSpyEl).refresh()
})

Events

EventBeschreibung
activate.bs.scrollspyWird am Scroll-Element ausgelöst, sobald Scrollspy einen Anker aktiviert.
const firstScrollSpyEl = document.querySelector('[data-bs-spy="scroll"]')
firstScrollSpyEl.addEventListener('activate.bs.scrollspy', () => {
  // do something...
})