Codelab: Sidenav-Komponente erstellen

In diesem Codelab erfahren Sie, wie Sie eine responsive Komponente für die seitliche Navigation auf der Website erstellen. Wir erstellen die Komponente nach und nach. Zuerst HTML, dann CSS und schließlich JavaScript.

In meinem Blogpost Building a Sidenav component erfahren Sie mehr über die CSS-Webplattformfunktionen, die für die Entwicklung dieser Komponente ausgewählt wurden.

Einrichtung

  1. Klicken Sie auf Remix to Edit (Remix zum Bearbeiten), um das Projekt bearbeitbar zu machen.
  2. Öffnen Sie app/index.html.

HTML

Zuerst müssen Sie die Grundlagen der HTML-Einrichtung kennen, damit Sie Inhalte und einige Felder haben, mit denen Sie arbeiten können.

Fügen Sie den folgenden HTML-Code in das <body>-Tag ein.

<aside></aside>
<main></main>

Das <aside>-Element enthält das Navigationsmenü als ergänzendes Element zu <main>, das den primären Seiteninhalt enthält.

Als Nächstes füllen wir diese semantischen Elemente mit dem restlichen Seiteninhalt.

Fügen Sie dem <aside>-Element ein Navigationselement, einige Navigationslinks und einen Schließen-Link hinzu.

<aside>
  <nav>
    <h4>My</h4>
    <a href="#">Dashboard</a>
    <a href="#">Profile</a>
    <a href="#">Preferences</a>
    <a href="#">Archive</a>

    <h4>Settings</h4>
    <a href="#">Accessibility</a>
    <a href="#">Theme</a>
    <a href="#">Admin</a>
  </nav>

  <a href="#"></a>
</aside>

Links passen gut in <nav>-Elemente und <nav>-Elemente passen gut in <aside>-Seitenleisten. Wir können aber noch mehr tun, um uns zu verbessern.

Fügen Sie dem Hauptelement für den Inhalt einen Header und einen Artikel hinzu, um den Layoutinhalt semantisch zu erfassen.

<main>
  <header>
    <a href="#sidenav-open" class="hamburger">
      <svg viewBox="0 0 50 40">
        <line x1="0" x2="100%" y1="10%" y2="10%" />
        <line x1="0" x2="100%" y1="50%" y2="50%" />
        <line x1="0" x2="100%" y1="90%" y2="90%" />
      </svg>
    </a>
    <h1>Site Title</h1>
  </header>

  <article>
    {put some placeholder content here}
  </article>
</main>

In der Kopfzeile befindet sich der Link zum Öffnen des Menüs. Die Schließen-Schaltfläche befindet sich im Aside-Element. Wir werden Elemente bald basierend auf der Größe des Viewports ein- und ausblenden.

Im <article>-Element haben wir einen Platzhaltersatz eingefügt. Ersetzen Sie `` durch Ihre eigenen Inhalte oder fügen Sie den unten stehenden Lorem-Text ein:

<h2>Totam Header</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Cum consectetur, necessitatibus velit officia ut impedit veritatis temporibus soluta? Totam odit cupiditate facilis nisi sunt hic necessitatibus voluptatem nihil doloribus! Enim.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>

<h3>Subhead Totam Odit</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>

<h3>Subhead</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>

Diese Inhalte und ihre Länge führen dazu, dass die Seite gescrollt werden kann, wenn sie die Höhe des Darstellungsbereichs überschreitet.

Bisher haben Sie ein aside-Element mit einer Navigationsleiste, Links und einer Möglichkeit zum Schließen der Seitennavigation hinzugefügt. Außerdem haben Sie dem Hauptelement eine Überschrift, eine Möglichkeit zum Öffnen der Seitenleiste und einen Artikel hinzugefügt. Das ist sauber, semantisch und schon ziemlich zeitlos, aber wir können es für alle noch sauberer und klarer machen. Der Link zum Öffnen in der Seitenleiste könnte deutlicher gekennzeichnet sein.

Fügen Sie dem Link-Element zum Öffnen des Headers die Attribute title und aria-label hinzu:

<a href="#sidenav-open" class="hamburger">
<a href="#sidenav-open" title="Open Menu" aria-label="Open Menu" class="hamburger">

Auch das offene SVG-Symbol könnte deutlicher gekennzeichnet sein. Fügen Sie dem SVG-Element im geöffneten Link-Element die folgenden Attribute hinzu:

<svg viewBox="0 0 50 40">
<svg viewBox="0 0 50 40" role="presentation" focusable="false" aria-label="trigram for heaven symbol">

Der Schließen-Link in der Seitenleiste könnte deutlicher gekennzeichnet sein. Fügen Sie dem Linkelement zum Schließen der Seitennavigation die Attribute title und aria-label hinzu:

<a href="#"></a>
<a href="#" title="Close Menu" aria-label="Close Menu"></a>

CSS

Zeit, die Elemente zu platzieren. Der Hauptinhalt und die Seitenleiste sind direkte untergeordnete Elemente des <body>-Tags. Das ist also ein guter Ausgangspunkt.

Fügen Sie das folgende CSS in css/sidenav.css ein, damit das <body>-Element die untergeordneten Elemente anordnet.

body {
  display: grid;
  grid: [stack] 1fr / min-content [stack] 1fr;

  @media (max-width: 540px) {
    & > :matches(aside, main) {
      grid-area: stack;
    }
  }
}

Dieses Layout besagt im Wesentlichen: Erstelle eine benannte Zeile stack mit allem, was darin enthalten ist, und zwei Spalten in dieser Zeile, von denen die zweite ebenfalls stack heißt. Die erste Spalte sollte entsprechend ihrem minimalen Inhalt dimensioniert werden, die zweite Spalte kann den Rest einnehmen. Wenn der Darstellungsbereich dann auf 540px oder weniger beschränkt ist, platzieren Sie die Elemente für die Seitenleiste und den Hauptinhalt in derselben Zeile und Spalte, sodass sie in einem 1x1-Raster übereinander liegen.

Auf dieser Grundlage können wir jetzt den Status der URL-Leiste nutzen, um die Sichtbarkeit und den Übergangsstil der Seitenleiste zu ändern.

Aktualisieren Sie das Element <aside> in app/index.html:

<aside>
<aside id="sidenav-open">

So kann das CSS ein Element und den URL-Hash zusammen abgleichen. Das ist wichtig für die Verwendung von :target. Die ID des Elements kann jetzt mit dem URL-Hash übereinstimmen, den wir mit <a>-Tags festlegen.

Außerdem sollten Sie IDs für wichtige Elemente hinzufügen, die die Sidenav steuern, um das JavaScript-Targeting zu vereinfachen. Fügen Sie zuerst dem Link zum Öffnen der Seitennavigation eine ID hinzu:

<a href="#sidenav-open" class="hamburger" title="Open Menu" aria-label="Open Menu">
<a href="#sidenav-open" id="sidenav-button" class="hamburger" title="Open Menu" aria-label="Open Menu">

Fügen Sie als Nächstes dem Link zum Schließen der Seitenleiste eine ID hinzu:

<a href="#" title="Close Menu" aria-label="Close Menu"></a>
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>

Damit haben wir das Makro <body> für das responsive Stapellayout abgeschlossen. Außerdem haben wir die URL-Leiste eingebunden. Weiter gehts!

Auch das <aside> hat ein übersichtliches Layout. Es hat zwei untergeordnete Elemente: ein <nav>-Element, das die papierähnliche Komponente darstellt, die herausgeschoben wird, und ein schließendes <a>-Link-Element, das die URL auf # festlegt. Der Link ist rechts neben der Navigationsleiste mit dem Papier-Tab ausgeblendet, damit Nutzer die visuelle Komponente durch Klicken auf eine andere Stelle schließen können.

Fügen Sie css/sidenav.css das folgende CSS hinzu:

#sidenav-open {
  display: grid;
  grid-template-columns: [nav] 2fr [escape] 1fr;
}

Das Verhältnis und die Namen sind hier wirklich schön. Das Raster kann hier seine Stärken ausspielen und dem Designer viel Kontrolle geben.

Als Nächstes muss ich den Hauptinhalt bedingt überlagern und meine Position beim Scrollen im Dokument beibehalten. Das ist ein toller Job für position: sticky und einige overscroll-behavior.

Fügen Sie die folgenden Stile für die Seitenleiste hinzu:

#sidenav-open {
  display: grid;
  grid-template-columns: [nav] 2fr [escape] 1fr;

  @media (max-width: 540px) {
    position: sticky;
    top: 0;
    max-height: 100vh;
    overflow: hidden auto;
    overscroll-behavior: contain;

    visibility: hidden; /* not keyboard accessible when closed */
  }
}

Diese Stile sorgen dafür, dass die Seitenleiste die Höhe des Darstellungsbereichs hat, vertikal gescrollt wird und den Scroll enthält. Das Element wird ausgeblendet. Standardmäßig wird die Seitenleiste ausgeblendet, wenn der Darstellungsbereich 540px oder kleiner ist. Es sei denn…

Fügen Sie dem #sidenav-open-Element den Pseudoselektor :target hinzu:

#sidenav-open {

  @media (max-width: 540px) {

    &:target {
      visibility: visible;
    }
  }
}

Wenn die ID dieses Elements und die URL-Leiste identisch sind, setzen Sie visibility auf visible. Öffnen Sie das Seitenmenü, nachdem Sie die Seite gescrollt haben, oder versuchen Sie, die Seite zu scrollen, während das Seitenmenü geöffnet ist. Was ist Ihre Meinung dazu?

Fügen Sie das folgende CSS am Ende von app/sidenav.css hinzu:

#sidenav-button,
#sidenav-close {
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
  user-select: none;
  touch-action: manipulation;

  @media (min-width: 540px) {
    display: none;
  }
}

Diese Stile sind für unsere Schaltflächen zum Öffnen und Schließen vorgesehen. Sie geben die Tipp- und Touch-Stile an und blenden die Schaltflächen bei Viewports mit einer Breite von 540px oder mehr aus.

Um das Ganze etwas aufzupeppen, fügen wir CSS-Transformationen hinzu, die die Barrierefreiheit berücksichtigen. Fügen Sie css/sidenav.css das folgende CSS hinzu:

#sidenav-open {
  --easeOutExpo: cubic-bezier(0.16, 1, 0.3, 1);
  --duration: .6s;

  ...

  @media (max-width: 540px) {
    ...

    transform: translateX(-110vw);
    will-change: transform;
    transition:
      transform var(--duration) var(--easeOutExpo),
      visibility 0s linear var(--duration);

    &:target {
      visibility: visible;
      transform: translateX(0);
      transition: transform var(--duration) var(--easeOutExpo);
    }
  }

  @media (prefers-reduced-motion: reduce) {
    --duration: 1ms;
  }
}
Eine Demo der Interaktion mit und ohne angewendete Dauer basierend auf der Media-Query „prefers-reduced-motion“.

JavaScript einfügen

Mit der Taste Escape sollte das Menü geschlossen werden. Fügen Sie diesen JS-Code zu js/index.js hinzu:

const sidenav = document.querySelector('#sidenav-open');

sidenav.addEventListener('keyup', e => {
  if (e.code === 'Escape') {
    document.location.hash = '';
  }
});

Damit wird auf ein Schlüsselereignis für das sidenav-Element gewartet. Wenn der Wert Escape ist, wird der URL-Hash auf „leer“ gesetzt und die Seitenleiste wird ausgeblendet.

Der nächste Teil von UX JS ist die Fokusverwaltung. Ich möchte das Öffnen und Schließen einfach gestalten. Deshalb warte ich, bis die Seitenleiste eine Übergangsanimation abgeschlossen hat, und vergleiche sie dann mit dem URL-Hash, um festzustellen, ob sie geöffnet oder geschlossen ist. Anschließend verwende ich JavaScript, um den Fokus auf die Schaltfläche zu setzen, die der Nutzer gerade nicht gedrückt hat.

Füge js/index.js das folgende JavaScript hinzu:

const closenav = document.querySelector('#sidenav-close');
const opennav = document.querySelector('#sidenav-button');

sidenav.addEventListener('transitionend', e => {
  if (e.propertyName !== 'transform') {
    return;
  }

  const isOpen = document.location.hash === '#sidenav-open';

  isOpen
    ? closenav.focus()
    : opennav.focus();
});

Jetzt ausprobieren

  • Wenn Sie sich eine Vorschau der Website ansehen möchten, drücken Sie App ansehen und dann Vollbild Vollbild.

Fazit

Das war alles, was ich mit der Komponente machen wollte. Sie können es gerne weiterentwickeln, es mit JavaScript-Status anstelle der URL steuern und es ganz allgemein an Ihre Bedürfnisse anpassen. Es gibt immer mehr hinzuzufügen oder mehr Anwendungsfälle abzudecken.

Öffne css/brandnav.css, um die nicht layoutbezogenen Stile zu sehen, die ich auf diese Komponente angewendet habe. Ich hielt es nicht für wichtig für die Funktionen, auf die ich mich konzentrierte, und hoffte, dass die Trennung von Stilen und Layout das Kopieren und Einfügen erleichtern würde. Vielleicht gibt es dort noch mehr für dich zu lernen.

Wie erstellen Sie responsive Sidenav-Komponenten, die eingeblendet werden? Hast du manchmal mehr als eine, z. B. auf beiden Seiten? Ich würde deine Lösung gerne in einem YouTube-Video vorstellen. Schreib mir auf Twitter oder kommentiere auf YouTube mit deinem Code. Das hilft allen!