Creazione di un componente Schede

Una panoramica di base su come creare un componente delle schede simile a quelli presenti nelle app per iOS e Android.

In questo post voglio condividere alcune idee sulla creazione di un componente Tabs per il web che sia reattivo, supporti più input di dispositivi e funzioni su tutti i browser. Prova la demo.

Demo

Se preferisci i video, ecco una versione di questo post su YouTube:

Panoramica

Le schede sono un componente comune dei sistemi di progettazione, ma possono assumere molte forme. In precedenza, le schede desktop erano basate sull'elemento <frame>, ma ora abbiamo componenti per dispositivi mobili fluidi che animano i contenuti in base alle proprietà fisiche. Tutti cercano di fare la stessa cosa: risparmiare spazio.

Oggi, gli elementi essenziali di un'esperienza utente con schede sono un'area di navigazione con pulsanti che attiva/disattiva la visibilità dei contenuti in un riquadro di visualizzazione. Molte diverse area di contenuti condividono lo stesso spazio, ma vengono presentate in modo condizionale in base al pulsante selezionato nella navigazione.

il collage è piuttosto caotico a causa dell&#39;enorme diversità di stili applicati dal web al concetto del componente
Un collage di stili di design web dei componenti delle schede degli ultimi 10 anni

Tattiche sul web

Nel complesso, ho trovato questo componente abbastanza semplice da creare, grazie a alcune funzionalità fondamentali della piattaforma web:

  • scroll-snap-points per interazioni eleganti con scorrimento e tastiera con posizioni di arresto dello scorrimento appropriate
  • Link diretti tramite hash dell'URL per supportare la condivisione e l'ancoraggio dello scorrimento in-page gestito dal browser
  • Supporto per screen reader con markup degli elementi <a> e id="#hash"
  • prefers-reduced-motion per attivare le transizioni con dissolvenza incrociata e lo scorrimento istantaneo all'interno della pagina
  • La funzionalità web @scroll-timeline in bozza per sottolineare e cambiare dinamicamente il colore della scheda selezionata

Il codice HTML

Fondamentalmente, l'esperienza utente è la seguente: fai clic su un link, l'URL rappresenta lo stato della pagina nidificata e poi vedi l'aggiornamento dell'area dei contenuti quando il browser scorre fino all'elemento corrispondente.

Sono presenti alcuni elementi di contenuti strutturali: link e :target. Abbiamo bisogno di un elenco di link, per il quale è ideale un <nav>, e di un elenco di elementi <article>, per il quale è ideale un <section>. Ogni hash del link corrisponderà a una sezione, consentendo al browser di scorrere i contenuti tramite l'ancoraggio.

Viene fatto clic su un pulsante di collegamento, che fa scorrere i contenuti in primo piano

Ad esempio, facendo clic su un link, l'articolo :target viene automaticamente messo a fuoco in Chrome 89, senza bisogno di JS. L'utente può quindi scorrere i contenuti dell'articolo con il suo dispositivo di input come sempre. Si tratta di contenuti aggiuntivi, come indicato nel markup.

Ho utilizzato il seguente markup per organizzare le schede:

<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>

Posso stabilire connessioni tra gli elementi <a> e <article> con le proprietà href e id come segue:

<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>

Poi ho riempito gli articoli con quantità diverse di Lorem e i link con titoli di lunghezza e immagini diverse. Con i contenuti su cui lavorare, possiamo iniziare il layout.

Layout scorrevoli

Questo componente contiene tre diversi tipi di aree di scorrimento:

  • La barra di navigazione (rosa) è scorrevole orizzontalmente
  • L'area dei contenuti (blu) è scorrevole orizzontalmente
  • Ogni articolo (verde) è scorrevole verticalmente.
Tre caselle colorate con frecce direzionali in tinta che contornano le aree di scorrimento e mostrano la direzione di scorrimento.

Esistono due tipi di elementi coinvolti nello scorrimento:

  1. Una finestra
    Una casella con dimensioni definite che ha lo stile della proprietà overflow.
  2. Una superficie di grandi dimensioni
    In questo layout, si tratta dei contenitori dell'elenco: link di navigazione, articoli della sezione e contenuti degli articoli.

Layout <snap-tabs>

Il layout di primo livello che ho scelto era flessibile (Flexbox). Ho impostato la direzione su column, quindi l'intestazione e la sezione sono ordinate verticalmente. Questa è la nostra prima finestra di scorrimento e nasconde tutto con overflow nascosto. A breve, l'intestazione e la sezione utilizzeranno lo scorrimento oltre il limite, come singole zone.

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; } }

Torna al diagramma colorato con tre scorrimenti:

  • <header> è ora pronto per essere il contenitore scorrevole (rosa).
  • <section> è pronto per essere il contenitore scorrevole (blu).

I frame che ho evidenziato di seguito con VisBug ci aiutano a vedere le finestre create dai contenitori di scorrimento.

Gli elementi di intestazione e sezione sono sovrapposti in hotpink, che ne delinea lo spazio occupato nel componente

Layout <header> schede

Il layout successivo è quasi lo stesso: utilizzo Flex per creare un ordinamento verticale.

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

.snap-indicator deve spostarsi in orizzontale insieme al gruppo di link. Questo layout dell'intestazione consente di impostare lo scenario. Nessun elemento con posizionamento assoluto.

Gli elementi nav e span.indicator hanno overlay hotpink che ne delineano lo spazio occupato nel componente

Poi, gli stili di scorrimento. A quanto pare possiamo condividere gli stili di scorrimento tra le due aree di scorrimento orizzontali (intestazione e sezione), quindi ho creato una classe di utilità, .scroll-snap-x.

.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;
    }
  }
}

Ognuno richiede uno spazio aggiuntivo sull'asse x, un contenimento dello scorrimento per bloccare lo scorrimento eccessivo, barre di scorrimento nascoste per i dispositivi touch e infine lo snap dello scorrimento per bloccare le aree di presentazione dei contenuti. L'ordine delle schede della tastiera è accessibile e tutte le interazioni consentono di concentrarsi in modo naturale. I contenitori con scorrimento automatico offrono anche un'interazione di tipo carosello con la tastiera.

Layout intestazione schede <nav>

I link di navigazione devono essere disposti in una riga, senza interruzioni di riga, centrati verticalmente e ogni elemento del link deve essere agganciato al contenitore di scorrimento. Lavori rapidamente per il 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;
  }
}

Ogni link ha una dimensione e uno stile specifico, quindi il layout di navigazione deve solo specificare la direzione e il flusso. Le larghezze uniche negli elementi di navigazione rendono divertente la transizione tra le schede in quanto l'indicatore regola la propria larghezza in base al nuovo target. A seconda del numero di elementi, il browser mostrerà o meno una barra di scorrimento.

Gli elementi a del menu di navigazione hanno overlay hotpink che ne delineano lo spazio occupato nel componente e dove si verifica l&#39;overflow

Layout schede <section>

Questa sezione è un articolo flessibile e deve essere il consumatore dominante di spazio. Deve inoltre creare colonne in cui inserire gli articoli. Ancora una volta, un rapido lavoro per CSS 2021. block-size: 100% estende questo elemento in modo da riempire il più possibile l'elemento principale; quindi, per il proprio layout, crea una serie di colonne che hanno una larghezza pari a 100% rispetto a quella principale. Le percentuali sono perfette in questo caso perché abbiamo scritto vincoli rigidi per l'elemento principale.

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%;
}

È come se dicessimo "espandi verticalmente il più possibile, in modo aggressivo" (ricorda l'intestazione impostata su flex-shrink: 0: è una difesa contro questa spinta all'espansione), che imposta l'altezza della riga per un insieme di colonne a altezza intera. Lo stile auto-flow indica alla griglia di disporre sempre gli elementi secondari in una riga orizzontale, senza a capo, esattamente ciò che vogliamo; per andare oltre la finestra principale.

Gli elementi dell&#39;articolo presentano overlay di colore rosa acceso, che indicano lo spazio occupato nel componente e i punti in cui si esauriscono

A volte faccio fatica ad arrossire la testa. Questo elemento della sezione si inserisce in una casella, ma ha anche creato un insieme di caselle. Spero che le immagini e le spiegazioni ti siano utili.

Layout <article> schede

L'utente deve essere in grado di scorrere i contenuti dell'articolo e le barre di scorrimento devono essere visualizzate solo in caso di overflow. Questi elementi dell'articolo sono in una posizione ordinata. Sono contemporaneamente elementi principali e secondari dello scorrimento. Il browser sta gestendo alcune interazioni complesse con tocco, mouse e tastiera.

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;
}

Ho scelto di far scattare gli articoli all'interno dello scorrevole principale. Mi piace molto il modo in cui gli elementi dei link di navigazione e gli elementi degli articoli si agganciano all'avvio in linea dei rispettivi contenitori di scorrimento. È come un rapporto armonioso.

L&#39;elemento article e i suoi elementi secondari sono sovrapposti in hotpink, che ne delinea lo spazio occupato nel componente e la direzione di overflow

L'articolo è un elemento secondario della griglia e le sue dimensioni sono predeterminate come l'area dell'area visibile che vogliamo fornire l'esperienza utente di scorrimento. Ciò significa che non ho bisogno di stili di altezza o larghezza, devo solo definire come si verifica il superamento. Ho impostato overflow-y su auto, e poi ho intrappolato anche le interazioni con lo scorrimento con la pratica proprietà overscroll-behavior.

Riepilogo delle 3 aree di scorrimento

Di seguito ho scelto l'opzione "Mostra sempre le barre di scorrimento" nelle impostazioni di sistema. Penso che sia molto importante che il layout funzioni con questa impostazione attivata, poiché spetta a me esaminare il layout e l'orchestrazione dello scorrimento.

Le tre barre di scorrimento sono impostate per essere visualizzate, ora occupano spazio nel layout e il nostro componente ha ancora un aspetto eccezionale

Penso che la visualizzazione della barra laterale in questo componente aiuti a mostrare chiaramente dove si trovano le aree di scorrimento, la direzione supportata e il modo in cui interagiscono tra di loro. Valuta in che modo ciascuno di questi frame della finestra di scorrimento è anche un elemento principale flex o grid di un layout.

DevTools può aiutarci a visualizzare quanto segue:

le aree di scorrimento hanno overlay di strumenti di griglia e flexbox che ne delineano lo spazio occupato nel componente e la direzione di overflow
Chromium DevTools, che mostra il layout dell'elemento di navigazione flexbox contenente elementi di ancoraggio, il layout della sezione della griglia contenente elementi di articolo e gli elementi di articolo contenenti paragrafi e un elemento di intestazione.

I layout di scorrimento sono completi: con aggancio, collegabili tramite link diretti e accessibili da tastiera. Una base solida per miglioramenti dell'esperienza utente, stile e soddisfazione.

Funzionalità in evidenza

I riquadri secondari a scorrimento bloccati mantengono la posizione bloccata durante il ridimensionamento. Ciò significa che JavaScript non dovrà visualizzare nulla durante la rotazione dei dispositivi o le dimensioni del browser. Prova la funzionalità nella Modalità dispositivo di Chromium DevTools selezionando una modalità diversa da Adattabile e ridimensionando il riquadro del dispositivo. Nota che l'elemento rimane visibile e bloccato con i suoi contenuti. Questa funzionalità è stata disponibile da quando Chromium ha aggiornato la propria implementazione in modo da renderla conforme alle specifiche. Ecco un post del blog in merito.

Animazione

L'obiettivo dell'animazione è collegare chiaramente le interazioni con il feedback della UI. In questo modo, l'utente può trovare (si spera) senza problemi tutti i contenuti. aggiungerò il movimento con uno scopo e condizionale. Ora gli utenti possono specificare le proprie preferenze di movimento nel sistema operativo e mi piace molto rispondere alle loro preferenze nelle mie interfacce.

Collegherò un'evidenziazione della scheda alla posizione di scorrimento dell'articolo. L'aggancio non è solo un allineamento estetico, ma serve anche ad ancorare l'inizio e la fine di un'animazione. In questo modo, <nav>, che funge da mini-mappa, rimane collegato ai contenuti. Controlleremo la preferenza di movimento dell'utente sia da CSS che da JS. Esistono alcuni posti fantastici da tenere in considerazione.

Comportamento di scorrimento

C'è un'opportunità per migliorare il comportamento dei movimenti sia di :target sia di element.scrollIntoView(). Per impostazione predefinita, è istantaneo. Il browser imposta solo la posizione di scorrimento. E se volessimo passare a quella posizione di scorrimento, instead of blink there?

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

Poiché stiamo introducendo il movimento, e un movimento che l'utente non controlla (come lo scorrimento), applichiamo questo stile solo se l'utente non ha preferenze nel suo sistema operativo in merito al movimento ridotto. In questo modo, introduciamo lo scorrimento di scorrimento solo per le persone che ne sono d'accordo.

Indicatore delle schede

Lo scopo di questa animazione è aiutare ad associare l'indicatore allo stato degli elementi. Ho deciso di applicare stili di transizione sfumata border-bottom a colori per gli utenti che preferiscono ridurre il movimento e un'animazione di scorrimento con transizione sfumata e scorrimento collegato per gli utenti che non hanno problemi con il movimento.

In Chromium DevTools, posso attivare/disattivare la preferenza e mostrare i 2 diversi stili di transizione. Mi sono divertito molto a realizzarlo.

@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;
  }
}

Nascondo il pulsante .snap-indicator quando l'utente preferisce la modalità Riduzione movimento, poiché non mi serve più. Poi lo sostituisco con gli stili border-block-end e un transition. Inoltre, nell'interazione con le schede, tieni presente che l'elemento di navigazione attivo non solo ha un'evidenziazione del brand in sottolineato, ma anche il colore del testo è più scuro. L'elemento attivo ha un contrasto del colore del testo più elevato e un'illuminazione sottoscocca brillante.

Bastano alcune righe di CSS aggiuntive per far sentire una persona vista (nel senso che rispettiamo attentamente le sue preferenze di movimento). Mi piace un sacco.

@scroll-timeline

Nella sezione precedente ti ho mostrato come gestisco gli stili di transizione graduale con movimento ridotto e in questa sezione ti mostrerò come ho collegato l'indicatore e un'area di scorrimento. Ecco alcune funzionalità sperimentali divertenti che verranno implementate a breve. Spero che tu sia tanto entusiasta quanto me.

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

Per prima cosa controllo la preferenza di movimento dell'utente da JavaScript. Se il risultato è false, che indica che l'utente preferisce la riduzione del movimento, non verranno eseguiti effetti di movimento che collegano gli elementi di scorrimento.

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

Al momento della stesura di questo documento, il supporto dei browser per @scroll-timeline non è disponibile. Si tratta di una bozza di specifica con solo implementazioni sperimentali. Tuttavia, ha un polyfill che utilizzo in questa demo.

ScrollTimeline

Sebbene sia CSS sia JavaScript possano creare schemi di scorrimento, ho attivato JavaScript per poter utilizzare le misurazioni degli elementi in tempo reale nell'animazione.

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

Voglio che un elemento segua la posizione di scorrimento di un altro e, creando un ScrollTimeline, definisco il gestore del link di scorrimento, ovvero il scrollSource. In genere, un'animazione sul web viene eseguita in base a un intervallo di tempo globale, ma con un sectionScrollTimeline personalizzato in memoria posso cambiare tutto.

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

Prima di passare ai fotogrammi chiave dell'animazione, ritengo sia importante sottolineare che il cursore dello scorrimento, tabindicator, verrà animato in base a una sequenza temporale personalizzata, ovvero lo scorrimento della nostra sezione. Il collegamento viene completato, ma manca l'ingrediente finale, ovvero i punti stateful tra cui animarsi, noti anche come frame chiave.

Fotogrammi chiave dinamici

Esiste un modo molto efficace per creare animazioni con CSS puramente dichiarativo @scroll-timeline, ma l'animazione che ho scelto era troppo dinamica. Non è possibile eseguire la transizione tra le larghezze auto e non è possibile creare dinamicamente un numero di keyframe in base alla durata dei figli.

JavaScript sa come ottenere queste informazioni, quindi eseguiremo l'iterazione sui figli e acquisiremo i valori calcolati in fase di esecuzione:

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

Per ogni tabnavitem, destruttura la posizione offsetLeft e restituisci una stringa che la utilizza come valore translateX. Vengono creati 4 fotogrammi chiave di trasformazione per l'animazione. Lo stesso vale per la larghezza: a ciascuna viene chiesto qual è la larghezza dinamica e poi viene utilizzata come valore del fotogramma chiave.

Ecco un esempio di output, in base ai miei caratteri e alle preferenze del browser:

Fotogrammi chiave TranslateX:

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

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

Fotogrammi chiave della larghezza:

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

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

Per riepilogare la strategia, l'indicatore della scheda ora verrà animato su quattro fotogrammi chiave, a seconda della posizione di snap dello scorrimento dello scorrevole della sezione. I punti di aggancio creano una chiara demarcazione tra i fotogrammi chiave e contribuiscono notevolmente all'effetto sincronizzato dell'animazione.

la scheda attiva e la scheda non attiva vengono mostrate con overlay VisBug che mostrano i punteggi di contrasto che superano entrambi

L'utente avvia l'animazione durante l'interazione, vedendo la larghezza e la posizione dell'indicatore cambiare da una sezione all'altra, monitorando perfettamente con lo scorrimento.

Potresti non averlo notato, ma sono molto orgoglioso della transizione di colore quando l'elemento di navigazione evidenziato viene selezionato.

Il grigio più chiaro non selezionato appare ancora più arretrato quando l'elemento evidenziato ha un maggiore contrasto. È comune usare il colore di transizione per il testo, ad esempio al passaggio del mouse e quando viene selezionato, ma è necessario passare tale colore allo scorrimento, sincronizzato con l'indicatore di sottolineatura.

Ecco come ho fatto:

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

Ogni link di navigazione tra le schede ha bisogno di questa nuova animazione di colore, che segue la stessa sequenza temporale di scorrimento dell'indicatore di sottolineatura. Io uso la stessa sequenza temporale di prima: dal momento che il suo ruolo è emettere un segno di spunta sulla rotellina, possiamo usarlo in qualsiasi tipo di animazione. Come ho fatto prima, creo 4 fotogrammi chiave nel loop e restituisco i colori.

[...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)",
]

Il fotogramma chiave con il colore var(--text-active-color) evidenzia il link, altrimenti è un colore di testo standard. Il loop nidificato in questo punto rende il tutto relativamente semplice, dato che il loop esterno è ogni elemento di navigazione e il loop interno è i fotogrammi chiave personali di ogni elemento di navigazione. Controllo se l'elemento loop esterno è uguale al loop interno e lo uso per sapere quando è selezionato.

Mi sono divertito molto a scriverlo. Tantissimo.

Ulteriori miglioramenti a JavaScript

Vale la pena ricordare che la parte principale di ciò che ti mostro qui funziona senza JavaScript. Detto questo, vediamo come possiamo migliorarlo quando JS è disponibile.

I link diretti sono più comunemente utilizzati per dispositivi mobili, ma credo che il loro scopo sia illustrato qui perché puoi condividere un URL con i contenuti di una scheda. Il browser in-page raggiungerà l'ID corrispondente nell'hash dell'URL. Ho scoperto che questo gestore onload ha applicato l'effetto su più piattaforme.

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

Sincronizzazione della fine dello scorrimento

I nostri utenti non fanno sempre clic o non usano sempre una tastiera, a volte semplicemente scorrono liberamente, come dovrebbero poter fare. Quando lo scorrimento della sezione si interrompe, la posizione in cui si ferma deve corrispondere a quella nella barra di navigazione in alto.

Ecco come attendo il termine dello scorrimento: js tabsection.addEventListener('scroll', () => { clearTimeout(tabsection.scrollEndTimer); tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100); });

Ogni volta che viene eseguito lo scorrimento delle sezioni, elimina il relativo timeout, se esistente, e avviane uno nuovo. Quando non viene più eseguito lo scorrimento delle sezioni, non cancellare il timeout e attivalo 100 ms dopo l'interruzione. Quando viene attivata, chiama la funzione che cerca di capire dove è stato interrotto l'utente.

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

  matchingNavItem && setActiveTab(matchingNavItem);
};

Supponendo che la scorrimento sia agganciata, la divisione della posizione di scorrimento attuale dalla larghezza dell'area di scorrimento dovrebbe restituire un numero intero e non un decimale. Poi provo a recuperare un navitem dalla nostra cache tramite questo indice calcolato e, se ne trova uno, invio la corrispondenza da impostare come attiva.

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

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

Per impostare la scheda attiva, inizia eliminando qualsiasi scheda attualmente attiva, quindi assegna all'elemento di navigazione in arrivo l'attributo dello stato attivo. La chiamata a scrollIntoView() ha un'interazione divertente con il CSS che vale la pena di notare.

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

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

Nel CSS dell'utilità di scorrimento orizzontale, abbiamo nidificato una query multimediale che applica lo scorrimento smooth se l'utente è resistente al movimento. JavaScript può richiamare liberamente gli elementi di scorrimento per visualizzarli, mentre il CSS può gestire la UX in modo dichiarativo. Un bel fiammifero che a volte fanno.

Conclusione

Ora che sai come ho fatto, come faresti? Ciò rende l'architettura dei componenti divertente. Chi creerà la prima versione con gli slot nel suo framework preferito? 🙂

Diversificaamo i nostri approcci e impariamo tutti i modi per creare sul web. Crea un Glitch e twittami la tua versione e la aggiungerò alla sezione Remix della community di seguito.

Remix della community