Tab-Komponente erstellen

Eine grundlegende Übersicht über die Erstellung einer Tab-Komponente ähnlich wie in iOS- und Android-Apps.

In diesem Beitrag möchte ich darüber sprechen, wie eine Tab-Komponente für das Web erstellt wird, die responsiv ist, die Eingabe mehrerer Geräte unterstützt und browserübergreifend funktioniert. Demo ansehen

Demo

Falls du lieber ein Video hast, findest du hier eine YouTube-Version dieses Beitrags:

Überblick

Registerkarten sind eine gängige Komponente von Designsystemen, können jedoch viele Formen und Formen annehmen. Zuerst gab es Desktop-Tabs, die auf dem <frame>-Element basieren, und jetzt gibt es einfache mobile Komponenten, die Inhalte basierend auf physikalischen Eigenschaften animieren. Alle versuchen, dasselbe Ziel zu erreichen: Platz zu sparen.

Heutzutage ist das Wesentlich für die Nutzerfreundlichkeit von Tabs der Navigationsbereich für Schaltflächen, mit dem die Sichtbarkeit der Inhalte in einem Anzeigeframe ein- und ausgeschaltet werden kann. Viele verschiedene Inhaltsbereiche teilen sich denselben Raum, werden aber basierend auf der in der Navigation ausgewählten Schaltfläche bedingt dargestellt.

Die Collage ist wegen der enormen Vielfalt an Stilen, die das Web auf das Komponentenkonzept anwendet, ziemlich chaotisch.
Eine Collage mit verschiedenen Webdesign-Stilen für Tabkomponenten aus den letzten 10 Jahren

Webtaktik

Insgesamt fand ich die Erstellung dieser Komponente dank einiger wichtiger Funktionen der Webplattform ziemlich einfach:

  • scroll-snap-points für elegante Wisch- und Tastaturinteraktionen mit entsprechenden Scroll-Stopp-Positionen
  • Deeplinks über URL-Hashes für die Unterstützung von In-Page-Scroll-Ankern und beim Teilen im Browser
  • Screenreader-Unterstützung mit den Elementen <a> und id="#hash"
  • prefers-reduced-motion zum Aktivieren von Überblendungen und sofortigem Scrollen auf der Seite
  • Die Webfunktion @scroll-timeline im Entwurf zum dynamischen Unterstreichen und Ändern der Farbe des ausgewählten Tabs

Der HTML-Code

Im Wesentlichen lautet die UX: Klicken Sie auf einen Link, lassen Sie die URL den Status der verschachtelten Seite darstellen und sehen Sie dann, wie der Inhaltsbereich aktualisiert wird, wenn der Browser zum übereinstimmenden Element scrollt.

Diese enthält einige Elemente für strukturelle Inhalte: Links und :targets. Wir benötigen eine Liste mit Links, für die sich ein <nav> gut eignet, und eine Liste von <article>-Elementen, für die sich ein <section> eignet. Jeder Link-Hash entspricht einem Abschnitt, sodass der Browser per Anker scrollen kann.

Es wird eine Link-Schaltfläche angeklickt, die sich in den fokussierten Inhalt gleitet

Wenn du zum Beispiel auf einen Link klickst, wird der :target-Artikel in Chrome 89 automatisch fokussiert. Es ist kein JavaScript erforderlich. Der Nutzer kann dann wie gewohnt mit seinem Eingabegerät durch den Artikelinhalt scrollen. Es handelt sich um ergänzende Inhalte, wie im Markup angegeben.

Ich habe die Tabs mithilfe des folgenden Markups organisiert:

<snap-tabs>
  <header>
    <nav>
      <a></a>
      <a></a>
      <a></a>
      <a></a>
    </nav>
  </header>
  <section>
    <article></article>
    <article></article>
    <article></article>
    <article></article>
  </section>
</snap-tabs>

So kann ich Verbindungen zwischen den Elementen <a> und <article> mit den Attributen href und id herstellen:

<snap-tabs>
  <header>
    <nav>
      <a href="#responsive"></a>
      <a href="#accessible"></a>
      <a href="#overscroll"></a>
      <a href="#more"></a>
    </nav>
  </header>
  <section>
    <article id="responsive"></article>
    <article id="accessible"></article>
    <article id="overscroll"></article>
    <article id="more"></article>
  </section>
</snap-tabs>

Als Nächstes füllte ich die Artikel mit gemischten Lorem-Mengen und Links mit unterschiedlichen Längen und Bildern. Mit den Inhalten, mit denen wir arbeiten können, beginnen wir mit dem Layout.

Layouts zum Scrollen

Bei dieser Komponente gibt es drei verschiedene Arten von Scrollbereichen:

  • Die Navigationsleiste (pink) ist horizontal scrollbar.
  • Der Inhaltsbereich (blau) ist horizontal scrollbar.
  • Jedes Artikelelement (grün) ist vertikal scrollbar.
Drei bunte Felder mit Richtungspfeilen, die die Scrollbereiche umranden und die Scrollrichtung umranden.

Beim Scrollen gibt es zwei verschiedene Arten von Elementen:

  1. Ein Fenster
    Ein Feld mit definierten Abmessungen, das den Eigenschaftenstil overflow hat.
  2. Übergroße Oberfläche
    In diesem Layout sind das die Listencontainer: Navigationslinks, Bereichsartikel und Artikelinhalte.

Layout: <snap-tabs>

Als Layout der obersten Ebene wählte ich das flexible Layout (Flexbox). Ich lege die Richtung auf column fest, damit Header und Abschnitt vertikal angeordnet sind. Dies ist unser erstes Scrollfenster. Es blendet alles mit ausgeblendetem Überlauf aus. Für den Header und den Abschnitt wird Overscroll bald als einzelne Zonen verwendet werden.

HTML
<snap-tabs>
  <header></header>
  <section></section>
</snap-tabs>
CSS
  snap-tabs {
  display: flex;
  flex-direction: column;

  /* establish primary containing box */
  overflow: hidden;
  position: relative;

  & > section {
    /* be pushy about consuming all space */
    block-size: 100%;
  }

  & > header {
    /* defend against 
needing 100% */ flex-shrink: 0; /* fixes cross browser quarks */ min-block-size: fit-content; } }

Zurück auf das bunte 3-Scroll-Diagramm:

  • <header> ist jetzt als Scrollcontainer (pink) vorbereitet.
  • <section> wird als (blau)-Scroll-Container vorbereitet.

Die Frames, die ich unten mit VisBug hervorgehoben habe, helfen uns, die Fenster zu sehen, die die Scroll-Container erstellt haben.

Die Header- und Abschnitt-Elemente haben Hotpink-Overlays, die den Platz umreißen, den sie in der Komponente einnehmen.

Layout der Tabs <header>

Das nächste Layout ist fast dasselbe: Ich verwende Flex für eine vertikale Anordnung.

HTML
<snap-tabs>
  <header>
    <nav></nav>
    <span class="snap-indicator"></span>
  </header>
  <section></section>
</snap-tabs>
CSS
header {
  display: flex;
  flex-direction: column;
}

Das .snap-indicator sollte sich horizontal mit der Gruppe von Links bewegen, und dieses Header-Layout hilft dabei, diese Phase festzulegen. Keine absolut positionierten Elemente vorhanden!

Die Elemente nav und span.indicator haben Hotpink-Overlays, die den Platz umreißen, den sie in der Komponente einnehmen.

Als Nächstes die Scroll-Stile. Wie sich herausstellt, können wir die Scrollstile zwischen unseren beiden horizontalen Scrollbereichen (Header und Abschnitt) teilen. Daher habe ich die Dienstprogrammklasse .scroll-snap-x erstellt.

.scroll-snap-x {
  /* browser decide if x is ok to scroll and show bars on, y hidden */
  overflow: auto hidden;
  /* prevent scroll chaining on x scroll */
  overscroll-behavior-x: contain;
  /* scrolling should snap children on x */
  scroll-snap-type: x mandatory;

  @media (hover: none) {
    scrollbar-width: none;

    &::-webkit-scrollbar {
      width: 0;
      height: 0;
    }
  }
}

Jeder benötigt einen Überlauf auf der x-Achse, eine Scroll-Begrenzung, um das Overscroll-Effekt zu verhindern, ausgeblendete Bildlaufleisten für Touch-Geräte und schließlich das Scroll-Andocken, um die Darstellungsbereiche für Inhalte zu sperren. Die TAB-Reihenfolge der Tastatur ist zugänglich und alle Interaktionsanleitungen sind natürlich fokussiert. Auch Scroll-Snap-Container haben eine nüchterne Karussell-Interaktion auf der Tastatur.

Layout der Tab-Kopfzeile <nav>

Die Navigationslinks müssen in einer Zeile ohne Zeilenumbrüche angeordnet und vertikal zentriert sein. Jedes Linkelement sollte am Scroll-Andock-Container einrasten. Swift-Arbeit für CSS 2021!

HTML
<nav>
  <a></a>
  <a></a>
  <a></a>
  <a></a>
</nav>
CSS
  nav {
  display: flex;

  & a {
    scroll-snap-align: start;

    display: inline-flex;
    align-items: center;
    white-space: nowrap;
  }
}

Jeder Linkstil und seine Größe müssen sich selbst anpassen, sodass im Navigationslayout nur Richtung und Ablauf angegeben werden müssen. Eindeutige Breiten von Navigationselementen machen den Übergang zwischen Tabs unterhaltsam, da die Breite der Anzeige an das neue Ziel angepasst wird. Je nachdem, wie viele Elemente sich hier befinden, rendert der Browser eine Bildlaufleiste oder nicht.

Die a-Elemente der Navigation haben Hotpink-Overlays, wodurch der Platz, den sie in der Komponente einnehmen, sowie die Stelle umrissen wird, an der sie überlaufen.

Layout der Tabs <section>

Dieser Abschnitt ist ein flexibles Element und muss der dominante Raumnutzer sein. Außerdem müssen Spalten erstellt werden, in die die Artikel eingefügt werden können. Nochmals für CSS 2021. Das block-size: 100% dehnt dieses Element so gestreckt, dass es das übergeordnete Element so weit wie möglich ausfüllt. Dann erstellt es für ein eigenes Layout eine Reihe von Spalten, deren 100% Breite des übergeordneten Elements ist. Prozentsätze sind hier gut, weil wir starke Beschränkungen für das übergeordnete Element festgelegt haben.

HTML
<section>
  <article></article>
  <article></article>
  <article></article>
  <article></article>
</section>
CSS
  section {
  block-size: 100%;

  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 100%;
}

Sie sagen so, als würden Sie „vertikal so viel wie möglich aufdringlich maximieren“ (denken Sie an den Header, den wir auf flex-shrink: 0 festgelegt haben: Er dient als Schutz vor dieser Erweiterungsverschiebung), bei der die Zeilenhöhe für eine Gruppe von Spalten in voller Höhe festgelegt wird. Mit dem Stil auto-flow wird das Raster angewiesen, die untergeordneten Elemente immer in einer horizontalen Linie anzulegen, ohne den Zeilenumbruch genau das, was wir möchten, und das übergeordnete Fenster zu überlaufen.

Die Artikelelemente haben Hotpink-Overlays, die den Platz, den sie in der Komponente einnehmen, und die Stelle umreißen, an der sie überlaufen.

Manchmal fällt es mir schwer, mich damit zu beschäftigen! Dieses „section“-Element passt in eine Box, erstellt aber auch ein Set von Boxen. Ich hoffe, die Bilder und Erklärungen helfen Ihnen weiter.

Layout der Tabs <article>

Der Nutzer sollte durch den Artikelinhalt scrollen können und die Bildlaufleisten sollten nur dann angezeigt werden, wenn ein Überlauf vorhanden ist. Die Artikelelemente sind übersichtlich positioniert. Sie sind gleichzeitig Scroll-Parent und Scroll-Child. Der Browser bewältigt einige knifflige Touch-, Maus- und Tastaturinteraktionen.

HTML
<article>
  <h2></h2>
  <p></p>
  <p></p>
  <h2></h2>
  <p></p>
  <p></p>
  ...
</article>
CSS
article {
  scroll-snap-align: start;

  overflow-y: auto;
  overscroll-behavior-y: contain;
}

Ich habe mich dafür entschieden, dass die Artikel im übergeordneten Scroller einrasten sollen. Besonders gut gefällt mir, wie die Elemente des Navigationslinks und die Artikelelemente am Inline-Anfang der jeweiligen Scroll-Container angedockt werden. Es wirkt wie eine harmonische Beziehung.

Das Artikelelement und seine untergeordneten Elemente haben Hotpink-Overlays, die den Platz, den sie in der Komponente einnehmen, sowie die Richtung, in die sie überlaufen, skizzieren.

Der Artikel ist ein untergeordnetes Raster und seine Größe ist vorab der Darstellungsbereich festgelegt, den wir Scroll-UX bieten möchten. Das bedeutet, ich benötige hier keine Höhen- oder Breitenstile. Ich muss nur definieren, wie es überläuft. Ich setze „overflow-y“ auf „auto“ und setze dann auch die Scrollinteraktionen mit der praktischen Property für das Overscroll-Verhalten über.

Zusammenfassung: 3 Scrollbereiche

In den Systemeinstellungen habe ich unten die Option "Bildlaufleisten immer anzeigen" ausgewählt. Ich denke, es ist doppelt wichtig, dass das Layout mit aktivierter Einstellung funktioniert, da ich mir das Layout und die Scrollorchestrierung genauer ansehe.

Die 3 Bildlaufleisten sind so eingestellt, dass sie angezeigt werden und belegen jetzt den Layoutbereich. Unsere Komponente sieht immer noch gut aus.

Durch den Spaltenabstand der Bildlaufleiste in dieser Komponente wird deutlich, wo sich die Scrollbereiche befinden, welche Richtung sie unterstützen und wie sie miteinander interagieren. Denken Sie daran, dass jeder dieser Scroll-Fenster-Frames auch einem Layout übergeordnete Flex- oder Raster-Frames darstellt.

Mit den Entwicklertools können wir dies visualisieren:

Die Scrollbereiche haben Raster- und Flexbox-Tool-Overlays, die den Platz, den sie in der Komponente einnehmen, sowie ihre Überlaufrichtung umreißen.
Chromium-Entwicklertools mit dem Layout des Flexbox-Navigationselements voller Ankerelemente, des Rasterbereichs mit allen Artikelelementen und den Artikelelementen voller Absätze und eines Überschriftenelements.

Die Scroll-Layouts sind vollständig: Andocken, Deeplink-fähig und Tastaturzugriff. Solide Grundlage für UX-Verbesserungen, -Stil und -Spaß.

Funktionshervorhebung

Kinder, die durch Klicken arretiert sind, behalten ihre gesperrte Position bei, während die Größe geändert wird. Das bedeutet, dass bei der Drehung des Geräts oder der Größenanpassung des Browsers bei JavaScript nichts sichtbar ist. Probiere die Funktion im Gerätemodus der Chromium-Entwicklertools aus. Wähle dazu einen anderen Modus als Responsiv aus und passe dann die Größe des Geräterahmens an. Beachten Sie, dass das Element weiterhin sichtbar und an seinem Inhalt fixiert bleibt. Dies war seit der Aktualisierung der Implementierung von Chromium verfügbar, damit sie den Spezifikationen entspricht. Weitere Informationen dazu findest du in diesem Blogpost.

Animation

Das Ziel der Animationsarbeit besteht darin, Interaktionen mit UI-Feedback klar zu verknüpfen. Dies hilft dem Nutzer dabei, alle Inhalte (hoffentlich) nahtlos zu entdecken. Ich werde zielführend und bedingt Bewegungselemente hinzufügen. Nutzer können jetzt in ihrem Betriebssystem ihre Präferenzen für Bewegungen festlegen und es macht mir großen Spaß, auf meinen Benutzeroberflächen auf ihre Einstellungen zu reagieren.

Ich verlinke einen unterstrichenen Tab mit der Scroll-Position des Artikels. Das Andocken ist nicht nur eine ziemliche Ausrichtung, es verankert auch den Anfang und das Ende einer Animation. Dadurch bleibt das <nav>, das wie eine Minikarte funktioniert, mit dem Inhalt verbunden. Wir überprüfen die Bewegungseinstellung des Nutzers über CSS und JS. Es gibt einige Orte, die berücksichtigt werden sollten.

Scrollverhalten

Es besteht die Möglichkeit, das Bewegungsverhalten von :target und element.scrollIntoView() zu verbessern. Standardmäßig erfolgt die Aktualisierung sofort. Der Browser legt lediglich die Scrollposition fest. Was ist, wenn wir zu dieser Scrollposition übergehen möchten, anstatt dort zu blinzeln?

@media (prefers-reduced-motion: no-preference) {
  .scroll-snap-x {
    scroll-behavior: smooth;
  }
}

Da wir hier Bewegung einführen und Bewegungen, über die der Nutzer keinen Einfluss hat (z. B. Scrollen), wenden wir diesen Stil nur an, wenn er in seinem Betriebssystem keine Präferenz bezüglich reduzierter Bewegung hat. So wird das Scrollen nur für Personen eingeführt, die damit einverstanden sind.

Tabanzeige

Durch diese Animation wird der Indikator mit dem Status des Inhalts in Verbindung gebracht. Ich beschloss, die border-bottom-Stile für Nutzer, die weniger Bewegung bevorzugen, farblich überblenden, für Nutzer, denen Bewegungsfreiheit nicht möglich ist, eine durch Scrollen verknüpfte Slide- und Farbausblendungsanimation.

In den Chromium-Entwicklertools kann ich die Einstellung aktivieren und deaktivieren und die beiden Übergangsstile demonstrieren. Das hat mir sehr viel Spaß gemacht.

@media (prefers-reduced-motion: reduce) {
  snap-tabs > header a {
    border-block-end: var(--indicator-size) solid hsl(var(--accent) / 0%);
    transition: color .7s ease, border-color .5s ease;

    &:is(:target,:active,[active]) {
      color: var(--text-active-color);
      border-block-end-color: hsl(var(--accent));
    }
  }

  snap-tabs .snap-indicator {
    visibility: hidden;
  }
}

Ich blende das .snap-indicator aus, wenn der Nutzer reduzierte Bewegung bevorzugt, da ich es nicht mehr benötige. Dann ersetze ich es durch border-block-end-Stile und transition. In der Interaktion mit den Tabs ist nicht nur die Marke unterstrichen, sondern auch die Textfarbe des aktiven Navigationselements dunkler. Das aktive Element hat einen höheren Textfarbkontrast und einen hellen Unterlicht-Akzent.

Schon mit ein paar zusätzlichen CSS-Zeilen fühlt sich eine Person gesehen (in dem Sinne, dass wir ihre Bewegungspräferenzen sorgfältig respektieren). Den finde ich super.

@scroll-timeline

Im obigen Abschnitt habe ich Ihnen gezeigt, wie ich mit den Stilen für reduzierte Bewegungsüberblendungen umgeht. In diesem Abschnitt zeige ich Ihnen, wie ich die Anzeige und einen Scrollbereich miteinander verknüpft habe. Als Nächstes haben wir spannende Experimente für Sie. Ich hoffe, Sie freuen sich genauso sehr wie ich.

const { matches:motionOK } = window.matchMedia(
  '(prefers-reduced-motion: no-preference)'
);

Zuerst prüfe ich die Bewegungseinstellungen des Nutzers in JavaScript. Wenn das Ergebnis false ist und der Nutzer eine reduzierte Bewegung bevorzugt, werden keine der Scroll-Verknüpfungen ausgeführt.

if (motionOK) {
  // motion based animation code
}

Zum Zeitpunkt der Erstellung dieses Dokuments ist keine Browserunterstützung für @scroll-timeline vorhanden. Es handelt sich um einen Spezifikationsentwurf, der ausschließlich experimentelle Implementierungen umfasst. Es hat allerdings einen Polyfill, den ich in dieser Demo verwende.

ScrollTimeline

Obwohl CSS und JavaScript beide Zeitachsen erstellen können, habe ich JavaScript aktiviert, damit ich Live-Elemente in der Animation verwenden konnte.

const sectionScrollTimeline = new ScrollTimeline({
  scrollSource: tabsection,  // snap-tabs > section
  orientation: 'inline',     // scroll in the direction letters flow
  fill: 'both',              // bi-directional linking
});

Ich möchte, dass eine Sache der Scrollposition einer anderen folgt. Durch das Erstellen eines ScrollTimeline definiere ich den Treiber des Scrolllinks, nämlich scrollSource. Normalerweise wird eine Animation im Web anhand eines globalen Zeitrahmens ausgeführt, aber mit einer benutzerdefinierten sectionScrollTimeline im Arbeitsspeicher kann ich all das ändern.

tabindicator.animate({
    transform: ...,
    width: ...,
  }, {
    duration: 1000,
    fill: 'both',
    timeline: sectionScrollTimeline,
  }
);

Bevor ich zu den Keyframes der Animation komme, ist es wichtig, die Person, die das Scrollen verfolgt, tabindicator, zu zeigen, das auf der Grundlage einer benutzerdefinierten Zeitachse animiert wird, dem Scrolling unseres Abschnitts. Damit ist die Verknüpfung abgeschlossen, es fehlen jedoch die zustandsorientierten Punkte, zwischen denen animiert werden müssen (auch Keyframes genannt).

Dynamische Keyframes

Mit @scroll-timeline kann eine wirklich leistungsstarke, reine deklarative CSS-Methode zum Animieren verwendet werden, aber die Animation, die ich erstellte, war zu dynamisch. Es gibt keine Möglichkeit, zwischen der auto-Breite zu wechseln, und es gibt keine Möglichkeit, dynamisch mehrere Keyframes basierend auf der Länge der untergeordneten Elemente zu erstellen.

Da JavaScript weiß, wie diese Informationen abgerufen werden können, iterieren wir selbst die untergeordneten Elemente und erfassen die berechneten Werte zur Laufzeit:

tabindicator.animate({
    transform: [...tabnavitems].map(({offsetLeft}) =>
      `translateX(${offsetLeft}px)`),
    width: [...tabnavitems].map(({offsetWidth}) =>
      `${offsetWidth}px`)
  }, {
    duration: 1000,
    fill: 'both',
    timeline: sectionScrollTimeline,
  }
);

Destrukturieren Sie für jeden tabnavitem die Position offsetLeft und geben Sie einen String zurück, der sie als translateX-Wert verwendet. Dadurch werden vier Transformations-Keyframes für die Animation erstellt. Dasselbe gilt für die Breite. Jedes Element wird nach seiner dynamischen Breite gefragt und dann als Keyframe-Wert verwendet.

Hier ist ein Ausgabebeispiel basierend auf meinen Schriftarten und Browsereinstellungen:

TranslateX-Keyframes:

[...tabnavitems].map(({offsetLeft}) =>
  `translateX(${offsetLeft}px)`)

// results in 4 array items, which represent 4 keyframe states
// ["translateX(0px)", "translateX(121px)", "translateX(238px)", "translateX(464px)"]

Keyframes für Breite:

[...tabnavitems].map(({offsetWidth}) =>
  `${offsetWidth}px`)

// results in 4 array items, which represent 4 keyframe states
// ["121px", "117px", "226px", "67px"]

Um es zusammenzufassen: Die Tab-Anzeige wird jetzt über vier Keyframes hinweg animiert, je nachdem, an welcher Position der Scroll-Dreh des Abschnitts gescrollt wird. Die Snappunkte erzeugen eine klare Abgrenzung zwischen den Keyframes und tragen wirklich zur synchronen Atmosphäre der Animation bei.

Der aktive und der inaktive Tab werden mit VisBug-Overlays angezeigt, die die bestandenen Kontrastwerte für beide anzeigen.

Der Nutzer steuert die Animation mit seiner Interaktion und sieht, wie sich die Breite und Position der Anzeige von einem Abschnitt zum nächsten ändern. Das Tracking erfolgt perfekt durch Scrollen.

Vielleicht haben Sie es noch nicht bemerkt, aber ich bin sehr stolz auf den Farbübergang, wenn das hervorgehobene Navigationselement ausgewählt wird.

Das nicht ausgewählte hellgraue Element wird noch stärker nach hinten verschoben, wenn das markierte Element mehr Kontrast hat. Es ist üblich, die Farbe für Text zu ändern, z. B. wenn der Mauszeiger darauf bewegt wird oder wenn die Option ausgewählt ist. Die nächste Ebene ist jedoch, diese Farbe beim Scrollen zu übertragen, synchron mit der Unterstreichungsanzeige.

So habe ich vorgegangen:

tabnavitems.forEach(navitem => {
  navitem.animate({
      color: [...tabnavitems].map(item =>
        item === navitem
          ? `var(--text-active-color)`
          : `var(--text-color)`)
    }, {
      duration: 1000,
      fill: 'both',
      timeline: sectionScrollTimeline,
    }
  );
});

Jeder Link zur Tab-Navigation benötigt diese neue Farbanimation, die dieselbe Scroll-Zeitachse verfolgt wie der Unterstrich-Indikator. Ich verwende die gleiche Zeitleiste wie zuvor: Da es beim Scrollen ein Häkchen ausgibt, können wir es in jeder beliebigen Wie zuvor erstelle ich vier Keyframes in der Schleife und gebe Farben zurück.

[...tabnavitems].map(item =>
  item === navitem
    ? `var(--text-active-color)`
    : `var(--text-color)`)

// results in 4 array items, which represent 4 keyframe states
// [
  "var(--text-active-color)",
  "var(--text-color)",
  "var(--text-color)",
  "var(--text-color)",
]

Der Keyframe mit der Farbe var(--text-active-color) hebt den Link hervor. Ansonsten ist es eine Standardtextfarbe. Da die verschachtelte Schleife dort relativ einfach ist, handelt es sich bei der äußeren Schleife um die einzelnen Navigationselemente und bei der inneren Schleife um die persönlichen Keyframes der einzelnen Elemente. Ich überprüfe, ob das Element der äußeren Schleife mit dem der inneren Schleife übereinstimmt, und dann weiß, wann es ausgewählt ist.

Es hat mir viel Spaß gemacht, das zu schreiben. Sehr.

Noch mehr JavaScript-Verbesserungen

Wir möchten Sie noch einmal daran erinnern, dass der Kern dessen, was ich Ihnen hier zeige, ohne JavaScript funktioniert. Sehen wir uns nun an, wie wir es verbessern können, wenn JS verfügbar ist.

Deeplinks sind eher ein mobiler Begriff, aber ich denke, dass der Deeplink hier mit Tabs erfüllt wird, da man eine URL direkt zum Inhalt eines Tabs teilen kann. Der Browser ruft auf der Seite die ID auf, die im URL-Hash übereinstimmt. Ich habe festgestellt, dass dieser onload-Handler den Effekt plattformübergreifend bewirkt hat.

window.onload = () => {
  if (location.hash) {
    tabsection.scrollLeft = document
      .querySelector(location.hash)
      .offsetLeft;
  }
}

Scrollen, Ende der Synchronisierung

Unsere Nutzer klicken nicht ständig auf die Schaltfläche oder verwenden eine Tastatur, manchmal können sie einfach nur scrollen, wie es ihnen möglich sein sollte. Wenn der Scroller des Abschnitts nicht mehr scrollt, muss die Position, an der er landet, in der oberen Navigationsleiste abgeglichen werden können.

So warte ich auf das Ende des Scrollens: js tabsection.addEventListener('scroll', () => { clearTimeout(tabsection.scrollEndTimer); tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100); });

Sobald durch die Abschnitte gescrollt werden, löschen Sie das entsprechende Zeitlimit für Bereiche und starten Sie ein neues. Wenn das Scrollen in Abschnitten beendet wird, löschen Sie das Zeitlimit nicht und werden 100 ms nach einer Pause ausgelöst. Wenn er ausgelöst wird, muss eine Funktion aufgerufen werden, um herauszufinden, an welcher Stelle der Nutzer aufgehört hat.

const determineActiveTabSection = () => {
  const i = tabsection.scrollLeft / tabsection.clientWidth;
  const matchingNavItem = tabnavitems[i];

  matchingNavItem && setActiveTab(matchingNavItem);
};

Angenommen, das Scrollen ist angedockt, würde das Teilen der aktuellen Scrollposition von der Breite des Scrollbereichs zu einer Ganzzahl und nicht zu einer Dezimalzahl führen. Dann versuche ich, über diesen berechneten Index ein navitem-Element aus unserem Cache abzurufen. Wenn es etwas findet, sende ich die Übereinstimmung, damit sie aktiviert wird.

const setActiveTab = tabbtn => {
  tabnav
    .querySelector(':scope a[active]')
    .removeAttribute('active');

  tabbtn.setAttribute('active', '');
  tabbtn.scrollIntoView();
};

Das Festlegen des aktiven Tabs beginnt damit, dass alle derzeit aktiven Tabs gelöscht werden und dem eingehenden Navigationselement dann das aktive Statusattribut zugewiesen wird. Der Aufruf von scrollIntoView() ermöglicht eine unterhaltsame Interaktion mit CSS, die erwähnt werden sollte.

.scroll-snap-x {
  overflow: auto hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;

  @media (prefers-reduced-motion: no-preference) {
    scroll-behavior: smooth;
  }
}

Im CSS-Dienstprogramm zum horizontalen Scrollen ist eine Medienabfrage verschachtelt, die smooth-Scrollen anwendet, wenn der Nutzer bewegungsfreundlich ist. JavaScript kann Aufrufe frei machen, um Elemente bis zu den sichtbaren Elementen zu scrollen, und CSS kann die UX deklarativ verwalten. Ein ganz schön entzückendes Paar, das manchmal zueinander passt.

Fazit

Jetzt, wo du weißt, wie ich es gemacht habe, wie würdest du es tun?! So entsteht eine unterhaltsame Component-Architektur! Wer erstellt die erste Version mit Slots in seinem Lieblings-Framework? 🙂

Diversifizieren wir unsere Ansätze und lernen Sie alle Möglichkeiten kennen, wie wir das Web nutzen können. Erstelle einen Glitch, twittere mich mit deiner Version und ich füge sie unten zum Abschnitt Community-Remixe hinzu.

Community-Remixe