Erstellen einer Smart-Navbar mit reinem JavaScript

Avatar of Jemima Abu
Jemima Abu am

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

Sticky oder feste Navigation ist eine beliebte Designwahl, da sie Benutzern einen dauerhaften Zugriff auf die Website ermöglicht. Andererseits nimmt sie Platz auf der Seite ein und verdeckt manchmal Inhalte auf eine weniger ansprechende Weise.

Eine mögliche Lösung? Intelligente Navigation.

Definieren wir „intelligente Navigation“ als

  1. Sichtbar am oberen Rand der Seite
  2. Sichtbar, wenn der Benutzer die Seite nach **oben** bewegt (wohin auch immer er gescrollt hat)
  3. Versteckt, wenn der Benutzer die Seite nach **unten** bewegt

Hier ist ein Beispiel, wie das funktionieren könnte

Es ist die gesamte Bequemlichkeit einer Sticky-Positionierung mit dem zusätzlichen Vollbildvorteil. Diese Art der intelligenten Navigation ist bereits üblich (denken Sie an die URL-Leiste in vielen mobilen Browsern), ist aber manchmal ohne Bibliothek oder Plugin mühsam zu implementieren. In diesem Artikel besprechen wir daher, wie man eine mit CSS und reinem JavaScript erstellt.

Seitennotiz: Menschen haben unterschiedliche Definitionen davon, was es bedeutet, eine Seite nach unten zu scrollen (stellen Sie sich vor, wie einige Trackpad-Einstellungen die Seite nach oben scrollen, wenn Sie Ihre Finger nach unten bewegen). Für die Zwecke dieses Artikels bezieht sich das Nach-unten-Scrollen auf die Bewegung in Richtung des unteren Seitenrands.

Schauen wir uns den Code an

Hier ist etwas Beispiel-HTML. Unsere Smart-Navbar wird das <nav> sein, das über dem <main> liegt.

<nav>
  <div class="logo">
    Logo
  </div>
  <div class="links">
    <a href="#">Link 1</a>
    <a href="#">Link 2</a>
    <a href="#">Link 3</a>
    <a href="#">Link 4</a>
  </div>
</nav>
<main>
  <!--Place the content of your page here-->
</main>

Es ist wichtig zu beachten, dass Elemente nur relativ zu ihrem übergeordneten Container sticky sind. Der übergeordnete Container von <nav> sollte das Body-Tag sein; er sollte nicht innerhalb eines anderen Tags auf der Seite platziert werden.

Das CSS für unsere Smart-Navbar sieht so aus

nav {
  position: sticky;
  top: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  padding: 1.5rem 2rem;
  background-color: #eaeaea;
}

Nun müssen wir erkennen, wann unser Benutzer die Seite scrollt und in welche Richtung er scrollt. Ein Benutzer scrollt nach unten, wenn der Wert seiner letzten Scroll-Position kleiner ist als der Wert seiner aktuellen Scroll-Position. Wenn wir die Logik aufschlüsseln, müssen wir

  1. Eine Variable definieren, um die vorherige Scroll-Position zu speichern
  2. Eine Variable zuweisen, um die aktuelle Scroll-Position zu erkennen, die auf den Scroll-Offset der Seite gesetzt ist

Wenn die aktuelle Scroll-Position größer ist als die vorherige Scroll-Position, dann scrollt der Benutzer nach unten. Nennen wir unsere Funktion isScrollingDown

let previousScrollPosition = 0;

const isScrollingDown = () => {
  let currentScrolledPosition = window.scrollY || window.pageYOffset;
  let scrollingDown;

  if (currentScrolledPosition > previousScrollPosition) {
    scrollingDown = true;
  } else {
    scrollingDown = false;
  }
  previousScrollPosition = currentScrolledPosition;
  return scrollingDown;
};

Hier ist eine visuelle Darstellung, wie diese Funktion funktioniert

Mit dieser Logik können wir erkennen, wann die Seite nach unten gescrollt wird, damit wir sie zum Umschalten unserer Navigationsstile verwenden können.

const nav = document.querySelector('nav');

const handleNavScroll = () => {
  if (isScrollingDown()) {
    nav.classList.add('scroll-down');
    nav.classList.remove('scroll-up')
  } else {
    nav.classList.add('scroll-up');
    nav.classList.remove('scroll-down')
  }
}

Wenn der Benutzer nach unten scrollt, weisen wir eine .scroll-down Klasse zu, die unsere Styling-Methode für den Fall enthält, dass sich die Seite nach unten bewegt. Wir können unser <nav> CSS auf Folgendes aktualisieren:

nav {
  /* default styling */
  transition: top 500ms ease-in-out;
}

nav.scroll-up {
  top: 0;
}

nav.scroll-down {
  top: -100%;
}

Mit diesem Styling wird der Wert der top-Eigenschaft von <nav> auf -100% der Seitenhöhe gesetzt, sodass es aus dem Sichtbereich herausgleitet. Wir könnten uns auch dafür entscheiden, unser Styling mit translate zu handhaben oder es auszublenden – welches Animation auch immer am besten funktioniert.

Performance

Immer wenn wir mit Scroll-Event-Listenern arbeiten, sollte die Leistung sofort in den Sinn kommen. Im Moment rufen wir unsere Funktion bei jeder Seitenbewegung auf, aber wir müssen nicht jede Pixelbewegung erfassen.

In diesem Fall können wir stattdessen eine Throttle-Funktion implementieren. Eine Throttle-Funktion ist eine Funktion höherer Ordnung, die als Timer für die an sie übergebene Funktion fungiert. Wenn wir ein Scroll-Ereignis mit einem Timer von 250 ms throtteln, wird das Ereignis nur alle 250 ms aufgerufen, während der Benutzer scrollt. Dies ist eine großartige Möglichkeit, die Anzahl der Funktionsaufrufe zu begrenzen und die Leistung der Seite zu verbessern.

David Corbacho geht in diesem Artikel tiefer auf Throttle-Implementierungen ein.

Eine einfache Throttle-Implementierung in JavaScript sieht so aus

// initialize a throttleWait variable
var throttleWait;

const throttle = (callback, time) => {
  // if the variable is true, don't run the function
  if (throttleWait) return;

  // set the wait variable to true to pause the function
  throttleWait = true;

  // use setTimeout to run the function within the specified time
  setTimeout(() => {
    callback();

    // set throttleWait to false once the timer is up to restart the throttle function
    throttleWait = false;
  }, time);
}

Dann können wir unsere handleNavScroll Funktion in ein Throttle einbinden

window.addEventListener("scroll", () => {
  throttle(handleNavScroll, 250)
});

Mit dieser Implementierung wird die handleNavScroll Funktion nur alle 250 ms einmal aufgerufen.

Barrierefreiheit (Accessibility)

Wenn wir eine benutzerdefinierte Funktion in JavaScript implementieren, müssen wir immer die Barrierefreiheit berücksichtigen. Ein solches Problem ist sicherzustellen, dass <nav> sichtbar ist, wenn es im Fokus ist. Browser neigen dazu, standardmäßig zu dem Teil der Seite zu scrollen, der gerade im Fokus ist, aber es kann bestimmte Komplikationen geben, wenn man mit Scroll-Ereignissen arbeitet.

Eine Möglichkeit, sicherzustellen, dass <nav> immer sichtbar ist, ist die Aktualisierung des CSS, um den Fokus zu berücksichtigen. Jetzt sieht unser CSS so aus:

nav.scroll-up,
nav:focus-within {
  top: 0;
}

Leider ist der focus-within-Selektor nicht vollständig in allen Browsern unterstützt. Wir können einen JavaScript-Fallback dafür einfügen

const handleNavScroll = () => {
  if (isScrollingDown() && !nav.contains(document.activeElement))) {
    nav.classList.add('scroll-down');
    nav.classList.remove('scroll-up')
  } else {
    nav.classList.add('scroll-up');
    nav.classList.remove('scroll-down')
  }
}

In dieser aktualisierten Funktion wenden wir die scroll-down Klasse nur an, wenn der Benutzer die Seite nach unten scrollt und die <nav> derzeit kein Element im Fokus hat.

Ein weiterer Aspekt der Barrierefreiheit ist die Überlegung, dass einige Benutzer möglicherweise keine Animationen auf der Seite wünschen. Das können wir mit der CSS-Media-Abfrage prefers-reduced-motion erkennen und respektieren. Wir können diese Methode in JavaScript aktualisieren und verhindern, dass unsere Funktion überhaupt ausgeführt wird, wenn ein Benutzer reduzierte Bewegung bevorzugt.

const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");

window.addEventListener("scroll", () => {
  if (mediaQuery && !mediaQuery.matches) {
    throttle(handleNavScroll, 250)
  }
});

Zusammenfassung

Damit haben wir eine intelligente Navigation mit reinem CSS und reinem JavaScript implementiert. Jetzt haben die Benutzer einen dauerhaften Zugriff auf die Website, ohne Platz zu verlieren, der den Inhalt blockiert.

Darüber hinaus ist der Vorteil einer solchen benutzerdefinierten Implementierung, dass wir ein hervorragendes Benutzererlebnis erhalten, das nicht übermäßig komplex ist und keine Leistung oder Barrierefreiheit beeinträchtigt.