Come costruire una giostra da zero in JS vaniglia.

Foto di Xavi Cabrera su Unsplash

Il ritmo della nuova tecnologia è incredibile. Così in fretta che è facile saltare al più recente e miglior framework o libreria senza dedicarsi al tempo necessario per comprendere veramente i fondamenti.

Avendo gestito e attualmente gestendo i team di sviluppo web, il mio obiettivo principale è la crescita personale del mio team. Una delle prime attività che ho impostato per gli sviluppatori in erba desiderosi di sviluppare le loro conoscenze di base su JS è costruire una giostra.

Sfida a carosello

Il brief è chiaro.

Crea una giostra funzionante nel codice leggibile dal browser. Niente librerie, niente framework, niente strumenti di compilazione. Solo HTML5, CSS3 e JavaScript.
Punti bonus se funziona con eventi touch sui dispositivi touchscreen.

Il motivo per cui mi piace impostare questo compito è perché la costruzione di una giostra richiede pensiero architettonico, gestione di dati ed elementi, manipolazione del DOM e considerazione per l'interazione dell'utente. Esistono così tanti modi per affrontare e imparare da questo compito che lo stesso sviluppatore, facendo questo stesso brief, con qualche mese di distanza, porterebbe a risultati completamente diversi.

Ora, basta parlare, iniziamo a capirlo.

Prima di scrivere qualsiasi codice, dobbiamo decidere come vogliamo che HTML, CSS e JS lavorino insieme.

Dovremmo passare le immagini come array in JS, dovremmo costruire l'HTML semantico in modo che la struttura sia presente prima del caricamento di JS, ci sarà l'animazione, in tal caso, sarà guidata da JS o CSS?

Questo esempio utilizzerà HTML strutturato, transizioni CSS3 e interattività controllata da JS.

HTML: struttura

Costruiremo la struttura per la nostra giostra. Mentre lo codifichiamo, dobbiamo considerare che potrebbe esserci più di un carosello su una singola pagina, quindi per motivi di riusabilità, useremo l'attributo class su id per targeting e styling.

Ecco la nostra struttura di base:

    
    
    
    
    
      
    
    
  

A partire dall'involucro del carosello, lo usiamo per ridimensionare il nostro carosello e nascondere qualsiasi contenuto di troppo pieno all'interno.

carousel conterrà tutti i nostri elementi come immagini e pulsanti di navigazione.

carousel__photo viene applicato ai nostri tag per uno stile semplice. Abbiamo anche dato l'immagine che vogliamo mostrare inizialmente una classe di iniziali.

carousel__button con i flag --next e --prev in modo da poter assegnare a ciascuno di essi il proprio evento click. Per coloro che sono interessati alle mie convenzioni sui nomi delle classi, sto seguendo la metodologia BEM.

HTML fatto. Al prossimo, CSS.

CSS: stili e transizioni

Sono un grande sostenitore di scrivere prima CSS per schermi più piccoli - AKA Mobile First), e quindi di utilizzare media query per espandere il design secondo i contenuti.

Con questo in mente, questo CSS sarà una vista a colonna singola del carosello e, per brevità, ometterò i prefissi dei fornitori (ad esempio-webkit-).

Facciamolo.

Immergersi con.-Carousel-wrapper, niente di speciale qui, solo trabocco e larghezza. L'idea qui è che la larghezza può essere cambiata per adattarsi al tuo contenuto e tutto al suo interno si ridimensionerà per adattarsi.

.carousel-wrapper {
  troppo pieno: nascosto;
  larghezza: 90%;
}

Vogliamo anche applicare border-box alla proprietà di ridimensionamento della casella in modo che eventuali imbottiture e bordi siano inclusi nella larghezza e altezza totali dei nostri elementi.

.carousel-wrapper * {
  dimensionamento scatola: bordo-scatola;
}

Utilizzeremo la proprietà di trasformazione per spostare gli elementi del nostro carosello, quindi l'impostazione dello stile di trasformazione su preserv-3d farà in modo che i nostri elementi nidificati vengano visualizzati correttamente nello spazio 3D.

.carousel {
  stile di trasformazione: preserv-3d;
}

Per impostazione predefinita, nascondiamo tutti gli elementi fino a quando lo script non avvia la sequenza. Per un facile posizionamento, gli elementi saranno posizionati in modo assoluto e avranno una larghezza reattiva del 100%. Il nostro contenuto determinerà l'altezza della giostra e la proprietà di transizione sarà la nostra magia per animare la giostra.

.carousel__photo {
  opacità: 0;
  posizione: assoluta;
  top: 0;
  larghezza: 100%;
  margine: auto;
  imbottitura: 1rem 4rem;
  indice z: 100;
  transizione: trasforma .5s, opacità .5s, indice z .5s;
}

A volte il caricamento degli script può richiedere un po 'di tempo, quindi mostriamo quello iniziale, rendiamolo relativo in modo che il contenitore padre si espanda e portarlo in primo piano usando z-index. Questi stili si applicheranno anche al nostro oggetto attivo per quando viene controllato il carosello.

.carousel__photo.initial,
.carousel__photo.active {
  opacità: 1;
  posizione: relativa;
  indice z: 900;
}

Durante la navigazione tra i caroselli, desideriamo che JS imposti dinamicamente le classi in modo da posizionare in anticipo gli elementi precedente e successivo traducendoli con la proprietà di trasformazione. Useremo di nuovo z-index per metterli in cima a qualsiasi altro oggetto, ma sotto l'elemento attivo.

.carousel__photo.prev,
.carousel__photo.next {
  indice z: 800;
}
.carousel__photo.prev {
  trasforma: translateX (-100%); / * Sposta l'elemento 'precedente' a sinistra * /
}
.carousel__photo.next {
  trasforma: translateX (100%); / * Sposta l'elemento "successivo" a destra * /
}

Il CSS per la giostra è fatto, tutto ciò che rimane sono i pulsanti di navigazione che si troveranno al centro, ai lati della giostra con le frecce all'interno. Invece di aggiungere altro HTML, aggiungeremo le frecce usando lo pseudo elemento :: after.

.carousel__button - prev,
.carousel__button - prossimo {
  posizione: assoluta;
  superiore: 50%;
  larghezza: 3rem;
  altezza: 3rem;
  colore di sfondo: #FFF;
  trasforma: translateY (-50%);
  raggio di frontiera: 50%;
  cursore: puntatore;
  indice z: 1001; / * Siediti sopra a tutto * /
  bordo: 1px nero solido;
}
.carousel__button - prev {
  a sinistra: 0;
}
.carousel__button - prossimo {
  a destra: 0;
}
.carousel__button - prev :: dopo,
.carousel__button - prossimo :: dopo {
  contenuto: "";
  posizione: assoluta;
  larghezza: 10px;
  altezza: 10px;
  in alto: 50%;
  sinistra: 54%;
  bordo-destra: 2px nero solido;
  bordo inferiore: 2px nero solido;
  trasforma: traduci (-50%, -50%) ruota (135 gradi);
}
.carousel__button - prossimo :: dopo {
  sinistra: 47%;
  trasforma: converti (-50%, -50%) ruota (-45 gradi);
}
Se hai seguito, la tua giostra non dovrebbe sembrare troppo diversa da questa.

JavaScript: farlo funzionare!

Se sei arrivato così lontano, ben fatto e grazie. Veramente. Grazie mille. Sei il migliore. ❤

Abbiamo la struttura, l'abbiamo nel modo in cui vogliamo che le transizioni raramente vadano, ora tutto ciò che dobbiamo fare è farlo funzionare.

È ora di capirlo prima di iniziare a scrivere codice.

  1. Dobbiamo avviare il carosello trovando l'oggetto iniziale e applicando la classe .prev e .next ai suoi oggetti adiacenti.
  2. Quindi vogliamo aggiungere eventi clic ai nostri pulsanti di navigazione.
  3. Gli eventi clic non sono nulla senza una funzione, quindi scriveremo due funzioni per gestire ogni direzione.
  4. Quando conosciamo la direzione in cui un utente sta tentando di navigare, scriviamo un'altra funzione per spostare il carosello in quella direzione.
  5. Per evitare che le persone facciano un clic eccessivo sui pulsanti, disabiliteremo l'interattività mentre l'animazione del carosello e la riabiliteremo al termine.
  6. Infine, vogliamo gestire tutto ciò in una funzione che sposta gli elementi nel nostro carosello scoprendo quali elementi aggiornare e li aggiorna con nuove classi per attivare le transizioni CSS3.

Per evitare conflitti e renderlo il più portatile possibile, sarà nel nostro interesse proteggere il nostro codice dall'ambito globale, quindi lo avvolgeremo in un IIFE.

! (Function (d) {
  // Tutto il codice verrà inserito qui. Abbiamo rinominato "document" in "d".
}(documento));

Per cominciare, dichiareremo le nostre variabili. Imposteremo una variabile come target della nostra classe base .carousel__photo, quindi memorizzeremo tutti gli oggetti con questa classe negli articoli, una volta memorizzati possiamo contarli e archiviare quel numero in totalItems, quindi imposteremo la diapositiva come la nostra corrente slide (all'indice 0) e per finire assegneremo lo spostamento come vero, che useremo per abilitare e disabilitare i clic sui pulsanti.

var itemClassName = "carousel__photo";
    items = d.getElementsByClassName (itemClassName),
    totalItems = items.length,
    slide = 0,
    moving = true;

Queste due funzioni successive impostano le classi iniziali e aggiungono i nostri listener di eventi ai pulsanti di navigazione.

// Imposta classi
function setInitialClasses () {
  // Targeting per gli elementi precedente, corrente e successivo
  // Ciò presuppone che ci siano almeno tre elementi.
  items [totalItems - 1] .classList.add ("prev");
  Articoli [0] .classList.add ( "attivo");
  articoli [1] .classList.add ( "next");
}
// Imposta listener di eventi
function setEventListeners () {
  var next = d.getElementsByClassName ('carousel__button - next') [0],
      prev = d.getElementsByClassName ('carousel__button - prev') [0];
  next.addEventListener ('click', moveNext);
  prev.addEventListener ('click', movePrev);
}

Stiamo associando moveNext e movePrev all'evento click, quindi creeremo meglio quelle funzioni successivamente. Queste funzioni controllano qualunque sia il numero di diapositiva corrente e lo incrementano, diminuiscono o lo impostano sul primo o sull'ultimo elemento.

// Gestore di navigazione successivo
funzione moveNext () {
  // Controlla se si sposta
  if (! moving) {
    // Se è l'ultima diapositiva, reimpostare su 0, altrimenti +1
    if (slide === (totalItems - 1)) {
      slide = 0;
    } altro {
      scorrere ++;
    }
    // Sposta il carosello nella diapositiva aggiornata
    moveCarouselTo (scivolo);
  }
}
// Gestore di navigazione precedente
funzione movePrev () {
  // Controlla se si sposta
  if (! moving) {
    // Se è la prima diapositiva, imposta come ultima diapositiva, altrimenti -1
    if (slide === 0) {
      slide = (totalItems - 1);
    } altro {
      diapositiva--;
    }
          
    // Sposta il carosello nella diapositiva aggiornata
    moveCarouselTo (scivolo);
  }
}

Eccezionale. Quindi siamo in grado di eseguire il codice, scriviamo un po 'di logica per gestire la variabile in movimento che lo imposta su true quando attivato, e quindi di nuovo su false al termine della transizione.

funzione disableInteraction () {
  // Imposta 'moving' su true per la stessa durata della nostra transizione.
  // (0,5 s = 500 ms)
  
  moving = true;
  // setTimeout esegue la sua funzione una volta dopo il tempo indicato
  setTimeout (function () {
    moving = false
  }, 500);
}

La funzione principale che gestirà l'intero carosello si trova all'interno di moveCarouselTo (diapositiva) che accetta un numero di diapositiva come argomento. Questa è la nostra più grande funzione, quindi ho commentato il codice.

funzione moveCarouselTo (slide) {
  // Controlla se la giostra si sta muovendo, in caso contrario, consenti l'interazione
  if (! moving) {
    // disabilita temporaneamente l'interattività
    disableInteraction ();
    // Aggiorna le "vecchie" diapositive adiacenti con "nuove"
    var newPrevious = slide - 1,
        newNext = slide + 1,
        oldPrevious = slide - 2,
        oldNext = slide + 2;
    // Verifica se la giostra contiene più di tre elementi
    if ((totalItems - 1)> 3) {
      // Verifica e aggiorna se le nuove diapositive sono fuori limite
      if (newPrevious <= 0) {
        oldPrevious = (totalItems - 1);
      } else if (newNext> = (totalItems - 1)) {
        oldNext = 0;
      }
      // Verifica e aggiorna se la diapositiva è all'inizio / alla fine
      if (slide === 0) {
        newPrevious = (totalItems - 1);
        oldPrevious = (totalItems - 2);
        oldNext = (slide + 1);
      } else if (slide === (totalItems -1)) {
        newPrevious = (slide - 1);
        newNext = 0;
        oldNext = 1;
      }
      // Ora abbiamo capito dove siamo e dove stiamo andando,
      // aggiungendo / rimuovendo le classi avvieremo le transizioni.
      // Ripristina i vecchi elementi next / prev sulle classi predefinite
      items [oldPrevious] .className = itemClassName;
      items [oldNext] .className = itemClassName;
      // Aggiungi nuove classi
      items [newPrevious] .className = itemClassName + "prev";
      items [slide] .className = itemClassName + "active";
      items [newNext] .className = itemClassName + "next";
    }
  }
}

Ci siamo quasi! C'è un'ultima funzione che dobbiamo svolgere e questa è quella che chiameremo per far funzionare tutto.

funzione initCarousel () {
  setInitialClasses ();
  setEventListeners ();
  // Imposta lo spostamento su falso in modo che la giostra diventi interattiva
  moving = false;
}

Infine, chiamiamo initCarousel () aggiungendolo in fondo:

// fa piovere
initCarousel ();

Se hai seguito, dovresti guardare - e interagire - con qualcosa del genere:

Spero ti sia piaciuto il mio articolo di debutto. Se l'hai trovato utile, ti preghiamo di condividerlo e se vuoi rimanere aggiornato puoi seguirmi su Instagram.