Sticky, Smooth, Active Nav

Avatar of Chris Coyier
Chris Coyier am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Wie der Titel schon sagt! Hier ist eine Seitenleisten-Navigationsleiste, die…

  1. Verwendet "sticky" Positionierung. Sie bleibt sichtbar, solange es möglich ist, aber überlappt nicht den Header, Footer und macht niemals Links unzugänglich.
  2. Scrollt sanft zu den Abschnitten, die Sie anklicken.
  3. Aktiviert die aktuelle Navigation basierend auf der Scroll-Position (es ist eine Einzelseiten-Sache).

Siehe den Pen Sticky, Smooth, Active Nav von Chris Coyier (@chriscoyier) auf CodePen.

Sticky

Es ist einfach, position: sticky; top: 0; auf etwas anzuwenden. Damit es funktioniert, muss es sich jedoch innerhalb eines höheren Elternelements befinden. Die ungeordnete Liste (<ul>) innerhalb der Navigation (<nav>) funktioniert hier also bestens. Dank des CSS-Grid-Layouts ist die <nav> so hoch wie der <main>-Inhaltsbereich. Beachten Sie jedoch, dass wir für iOS auch position: -webkit-sticky; benötigen.

Ich habe auch eine magische Zahl für die vertikale Media Query hinzugefügt, damit sie nicht so haftet, dass Sie die unteren Navigationspunkte nicht erreichen können.

/* Only stick if you can fit */
@media (min-height: 300px) {
  nav ul {
    position: sticky;
    top: 0;
  }
}

Smooth

Bei meinem ersten Versuch dachte ich an JavaScript-basiertes Smooth Scrolling. Es ist heutzutage sogar nativ, ohne dass Frameworks benötigt werden. Sie können ein Element ansteuern und sanft dorthin scrollen.

document.querySelector('.hello').scrollIntoView({ 
  behavior: 'smooth' 
});

Das auf eine beliebige Anzahl von Navigationspunkten anzuwenden…

let mainNavLinks = document.querySelectorAll("nav ul li a");

mainNavLinks.forEach(link => {
  link.addEventListener("click", event => {
    event.preventDefault();
    let target = document.querySelector(event.target.hash);
    target.scrollIntoView({
      behavior: "smooth",
      block: "start"
    });
  });
});

Das wird sowohl in Chrome als auch in Firefox unterstützt, aber nicht in Edge oder Safari.

Dann fiel mir ein, dass CSS das kann! Es gibt eine Eigenschaft scroll-behavior, die Sie dem Dokument zuweisen können, um alles auf diese Weise scrollen zu lassen.

html {
  scroll-behavior: smooth;
}

Da unsere Navigationslinks (<a>) Hash-/Sprung-/Ankerlinks sind, ist das buchstäblich alles, was wir brauchen. Vergessen Sie das JavaScript. Insbesondere da die Browserunterstützung für scroll-behavior dieselbe ist wie für die "Smooth"-Version von .scrollIntoView().

Active

Das ist etwas kniffliger, insbesondere weil dies eine Einzelseiten-Scrolling-App und keine einzelnen Seiten mit eigenen separaten Dokumenten sind. Wenn es sich um separate Dokumente handeln würde, würden wir irgendwo in der Navigation eine aktive Klasse ändern oder eine Klasse wie body.specific_page verwenden oder so etwas.

Stattdessen müssen wir die Scroll-Position der Seite betrachten, entscheiden, welcher Abschnitt sichtbar ist, und ihn entsprechend markieren. Es mag eine schicke IntersectionObserver-Methode geben, um das zu handhaben, aber ich konnte sie nicht ganz erfassen, also betrachte ich stattdessen alle relevanten Abschnitte, nehme einige Messungen und Berechnungen vor und entscheide auf diese Weise, ob der Link aktiv ist.

let mainNavLinks = document.querySelectorAll("nav ul li a");
let mainSections = document.querySelectorAll("main section");

let lastId;
let cur = [];

window.addEventListener("scroll", event => {
  let fromTop = window.scrollY;

  mainNavLinks.forEach(link => {
    let section = document.querySelector(link.hash);

    if (
      section.offsetTop <= fromTop &&
      section.offsetTop + section.offsetHeight > fromTop
    ) {
      link.classList.add("current");
    } else {
      link.classList.remove("current");
    }
  });
});

Der Scroll-Handler dort sollte ein kleines Warnsymbol auslösen. Das ist die Art von Sache, die wahrscheinlich gedrosselt werden sollte, wenn Sie lodash verfügbar haben.

window.addEventListener("scroll", () => {
  _.throttle(doThatStuff, 100);
});

Ich habe das hier nur nicht gemacht, um die Demo vom Abhängigkeiten zu befreien.

Oh! Und es funktioniert größtenteils gut auf Mobilgeräten (hier iOS).

Eine kostenlose Vorlage für JavaScript-Bibliotheks-Homepages

Ich habe all diese Dinge in dieser Vorlage verwendet, die Sie kostenlos nutzen können, für was auch immer.