Comment construire un carrousel à partir de zéro en vanille JS.

Photo de Xavi Cabrera sur Unsplash

Le rythme des nouvelles technologies publiées est incroyable. Si vite qu’il est facile de s’appuyer sur le dernier et le plus grand framework ou la plus grande bibliothèque sans prendre le temps de vraiment comprendre les principes fondamentaux.

Ayant géré et gérant actuellement des équipes de développement Web, mon objectif principal est la croissance personnelle de mon équipe. L'une des premières tâches que je me suis fixée pour les développeurs en herbe désireux de développer leurs connaissances de base de JS est de construire un carrousel.

Défi carrousel

Le brief est clair.

Construisez un carrousel fonctionnel en code lisible par le navigateur. Pas de bibliothèques, pas de frameworks, pas d'outils de construction. Juste HTML5, CSS3 et JavaScript.
Points bonus si cela fonctionne avec des événements tactiles sur des appareils à écran tactile.

J'adore définir cette tâche car la construction d'un carrousel nécessite une réflexion architecturale, la gestion des données et des éléments, la manipulation de DOM, ainsi que la prise en compte de l'interaction de l'utilisateur. Il y a tellement de façons d’aborder et d’apprendre de cette tâche que le même développeur, qui fait le même travail avec quelques mois d’intervalle, aboutirait à des résultats complètement différents.

Maintenant, assez parlé, commençons à comprendre cela.

Avant d'écrire un code, nous devons décider de la manière dont nous voulons que HTML, CSS et JS fonctionnent ensemble.

Devrions-nous passer les images sous forme de tableau dans JS, devrions-nous construire le code HTML sémantique afin que la structure existe avant le chargement de JS? Y aura-t-il une animation? Si oui, sera-t-elle pilotée par JS ou CSS?

Cet exemple utilisera du HTML structuré, des transitions CSS3 et une interactivité contrôlée par JS.

HTML: structure

Nous allons construire la structure de notre carrousel. En codant cela, nous devons considérer qu’il peut y avoir plus d’un carrousel sur une seule page. Par conséquent, pour des raisons de réutilisabilité, nous utiliserons l’attribut class sur id pour le ciblage et le style.

Voici notre structure de base:

  
    
    
    
    
    
      
    
  

En commençant par le carrousel-wrapper, nous l'utilisons pour dimensionner notre carrousel et masquer tout contenu en débordement de l'intérieur.

carrousel tiendra tous nos éléments comme des images et des boutons de navigation.

carrousel__photo est appliquée à nos balises pour un style facile. Nous avons également donné l'image que nous souhaitons afficher initialement une classe d'initiale.

carrousel__button avec les options --next et --prev afin que nous puissions donner à chacun leur propre événement de clic. Pour ceux intéressés par les conventions de nommage de mes classes, je suis la méthodologie BEM.

HTML fait. À côté, CSS.

CSS: Styles et transitions

Je suis un grand partisan de l’écriture de CSS pour les écrans les plus petits - AKA Mobile First), puis de l’utilisation de requêtes multimédia pour étendre la conception en fonction du contenu.

En gardant cela à l’esprit, ce CSS sera une vue à une colonne du carrousel et, par souci de brièveté, j’éliminerai les préfixes de vendeur (par exemple ,.-webkit-).

Faisons cela.

Plonger avec .carousel-wrapper, rien de spécial ici, juste un débordement et une largeur. L'idée ici est que la largeur peut être modifiée en fonction de votre contenu et que tout ce qu'il contient s'adapte à la taille.

.carousel-wrapper {
  débordement caché;
  largeur: 90%;
}

Nous voudrons également appliquer border-box à la propriété box-sizing afin que tout remplissage et bordure soient inclus dans la largeur et la hauteur totales de nos éléments.

.carousel-wrapper * {
  taille de la boîte: boîte à bordure;
}

Nous allons utiliser la propriété transform pour déplacer les éléments de notre carrousel. Par conséquent, si vous définissez le style de transformation sur préserv-3d, vous vous assurez que nos éléments imbriqués sont restitués correctement dans l’espace 3D.

.carousel {
  transformer-style: preserve-3d;
}

Par défaut, nous masquons tous les éléments jusqu'à ce que le script lance le carrousel. Pour un positionnement facile, les éléments seront absolument positionnés et auront une largeur de réponse de 100%. Notre contenu dictera la hauteur du carrousel et la propriété de transition sera notre magie pour rendre le carrousel animé.

.carousel__photo {
  opacité: 0;
  position: absolue;
  en haut: 0;
  largeur: 100%;
  marge: auto;
  rembourrage: 1rem 4rem;
  z-index: 100;
  transition: transformer .5s, opacité .5s, z-index .5s;
}

Parfois, le chargement des scripts peut prendre un peu de temps. Par conséquent, montrons le script initial, fixez-le de manière à ce que le conteneur parent se développe et placez-le au premier plan à l’aide de z-index. Ces styles s'appliqueront également à notre élément actif lorsque le carrousel est contrôlé.

.carousel__photo.initial,
.carousel__photo.active {
  opacité: 1;
  position: relative;
  z-index: 900;
}

Lors de la navigation dans les carrousels, nous souhaitons que JS configure de manière dynamique les classes afin de prépositionner les éléments précédent et suivant en les traduisant avec la propriété transform. Nous utiliserons à nouveau z-index pour les placer au-dessus de tout autre élément, mais en dessous de l’élément actif.

.carousel__photo.prev,
.carousel__photo.next {
  z-index: 800;
}
.carousel__photo.prev {
  transformer: translateX (-100%); / * Déplace l'élément 'prev' vers la gauche * /
}
.carousel__photo.next {
  transformer: translateX (100%); / * Déplace l'élément 'suivant' vers la droite * /
}

Le CSS du carrousel est terminé, il ne reste plus que les boutons de navigation qui seront placés au milieu, de chaque côté du carrousel, avec des flèches à l’intérieur. Au lieu d’ajouter plus de HTML, nous allons ajouter les flèches en utilisant le pseudo-élément :: after.

.carousel__button - prev,
.carousel__button - next {
  position: absolue;
  en haut: 50%;
  largeur: 3rem;
  hauteur: 3rem;
  couleur de fond: #FFF;
  transformer: traductionY (-50%);
  rayon de bordure: 50%;
  curseur: pointeur;
  z-index: 1001; / * Asseyez-vous au dessus de tout * /
  bord: 1px noir solide;
}
.carousel__button - prev {
  gauche: 0;
}
.carousel__button - next {
  à droite: 0;
}
.carousel__button - prev :: after,
.carousel__button - next :: after {
  contenu: " ";
  position: absolue;
  largeur: 10px;
  hauteur: 10px;
  en haut: 50%;
  à gauche: 54%;
  bordure droite: 2px noir uni;
  bordure inférieure: noir 2px;
  transformer: traduire (-50%, -50%) faire pivoter (135deg);
}
.carousel__button - next :: after {
  à gauche: 47%;
  transformer: traduire (-50%, -50%) faire pivoter (-45deg);
}
Si vous avez suivi, votre carrousel ne devrait pas sembler très différent de cela.

JavaScript: ça marche!

Si vous avez réussi jusque-là, bravo et merci. Vraiment. Merci beaucoup. Tu es le meilleur. ❤

Nous avons la structure, nous avons la structure que nous désirons, les transitions étant prêtes à disparaître, il ne nous reste plus qu'à la faire fonctionner.

Il est temps de comprendre cela avant de commencer à coder.

  1. Nous devons lancer le carrousel en recherchant l'élément initial et en appliquant les classes .prev et .next à ses éléments adjacents.
  2. Ensuite, nous voulons ajouter des événements de clic à nos boutons de navigation.
  3. Les événements de clic ne sont rien sans une fonction, nous allons donc écrire deux fonctions pour gérer chaque direction.
  4. Une fois que nous connaissons la direction dans laquelle un utilisateur tente de naviguer, écrivons une autre fonction pour déplacer le carrousel dans cette direction.
  5. Pour éviter que les utilisateurs ne cliquent trop sur les boutons, nous allons désactiver l’interactivité pendant l’animation du carrousel et l’activer à nouveau une fois terminé.
  6. Enfin, nous souhaitons gérer tout cela dans une fonction qui déplace les éléments de notre carrousel en déterminant quels éléments doivent être mis à jour, et les met à jour avec de nouvelles classes pour déclencher les transitions CSS3.

Pour éviter les conflits et rendre cette solution aussi portable que possible, il est dans notre intérêt de protéger notre code de la portée globale. Nous allons donc le résumer dans un IIFE.

! (fonction (d) {
  // Tout le code ira ici. Nous avons renommé "document" en "d".
}(document));

Pour commencer, nous allons déclarer nos variables. Nous allons définir une variable pour cibler notre classe de base .carousel__photo, puis stocker tous les objets de cette classe dans des éléments, une fois stockés, nous pouvons les compter et stocker ce nombre dans totalItems, puis nous allons définir la diapositive comme étant notre élément actuel. slide (à l'index 0), et pour finir, nous assignerons move à true, que nous utiliserons pour activer et désactiver les clics sur les boutons.

var itemClassName = "carrousel__photo";
    items = d.getElementsByClassName (itemClassName),
    totalItems = items.length,
    slide = 0,
    en mouvement = vrai;

Ces deux fonctions suivantes définissent les classes initiales et ajoutent nos écouteurs d'événements aux boutons de navigation.

// Définir les classes
fonction setInitialClasses () {
  // Cible les éléments précédents, actuels et suivants
  // Cela suppose qu'il y a au moins trois éléments.
  items [totalItems - 1] .classList.add ("prev");
  items [0] .classList.add ("active");
  items [1] .classList.add ("next");
}
// Définir les écouteurs d'événement
fonction setEventListeners () {
  var next = d.getElementsByClassName ('carrousel__button - next') [0],
      prev = d.getElementsByClassName ('carrousel__button - prev') [0];
  next.addEventListener ('click', moveNext);
  prev.addEventListener ('click', movePrev);
}

Nous lions moveNext et movePrev à l'événement click, nous devons donc créer ces fonctions par la suite. Ces fonctions vérifient le numéro de la diapositive en cours et l’incrémentent, la décrémentent ou la définissent sur le premier ou le dernier élément.

// gestionnaire de navigation suivant
fonction moveNext () {
  // Vérifie si déménage
  si (! en mouvement) {
    // Si c'est la dernière diapositive, remettre à 0, sinon +1
    if (slide === (totalItems - 1)) {
      slide = 0;
    } autre {
      slide ++;
    }
    // Déplacer le carrousel vers la diapositive mise à jour
    moveCarouselTo (diapositive);
  }
}
// Gestionnaire de navigation précédent
fonction movePrev () {
  // Vérifie si déménage
  si (! en mouvement) {
    // Si c'est la première diapositive, définir comme dernière diapositive, sinon -1
    if (slide === 0) {
      slide = (totalItems - 1);
    } autre {
      faire glisser--;
    }
          
    // Déplacer le carrousel vers la diapositive mise à jour
    moveCarouselTo (diapositive);
  }
}

Impressionnant. Nous sommes donc en mesure d’exécuter le code, écrivons un peu de logique pour gérer la variable en mouvement qui le définit à true lorsqu’il est déclenché, puis à nouveau à false une fois la transition terminée.

function disableInteraction () {
  // Définit 'moving' sur true pour la même durée que notre transition.
  // (0.5s = 500ms)
  
  en mouvement = vrai;
  // setTimeout exécute sa fonction une fois après l'heure indiquée
  setTimeout (function () {
    en mouvement = faux
  }, 500);
}

La fonction principale qui gérera tout le carrousel se trouve dans moveCarouselTo (diapositive), qui prend comme argument un numéro de diapositive. C’est notre fonction la plus importante et j’ai commenté le code.

fonction moveCarouselTo (slide) {
  // Vérifiez si le carrousel est en mouvement, sinon, autorisez l'interaction
  si (! en mouvement) {
    // désactiver temporairement l'interactivité
    disableInteraction ();
    // Met à jour les "anciennes" diapositives adjacentes avec les "nouvelles"
    var newPrevious = slide - 1,
        newNext = slide + 1,
        oldPrevious = slide - 2,
        oldNext = slide + 2;
    // Teste si le carrousel a plus de trois éléments
    si ((totalItems - 1)> 3) {
      // Vérifie et met à jour si les nouvelles diapositives sont hors limites
      if (newPrevious <= 0) {
        oldPrevious = (totalItems - 1);
      } else if (newNext> = (totalItems - 1)) {
        oldNext = 0;
      }
      // Vérifie et met à jour si la diapositive est au début / à la fin
      if (slide === 0) {
        newPrevious = (totalItems - 1);
        oldPrevious = (totalItems - 2);
        oldNext = (slide + 1);
      } sinon si (slide === (totalItems -1)) {
        newPrevious = (slide - 1);
        newNext = 0;
        oldNext = 1;
      }
      // Maintenant, nous avons déterminé où nous en sommes et où nous allons,
      // en ajoutant / supprimant des classes, nous déclencherons les transitions.
      // Réinitialiser les anciens éléments next / prev aux classes par défaut
      items [oldPrevious] .className = itemClassName;
      items [oldNext] .className = itemClassName;
      // Ajouter de nouvelles classes
      items [newPrevious] .className = itemClassName + "prev";
      items [slide] .className = itemClassName + "active";
      items [newNext] .className = itemClassName + "next";
    }
  }
}

Nous y sommes presque! Il nous reste une dernière fonction à remplir, c’est celle que nous allons appeler pour que tout fonctionne.

fonction initCarousel () {
  setInitialClasses ();
  setEventListeners ();
  // Définissez move sur false pour que le carrousel devienne interactif
  en mouvement = faux;
}

Enfin, appelons initCarousel () en l’ajoutant en bas:

// Fais qu'il pleuve
initCarousel ();

Si vous avez suivi, vous devriez chercher et interagir avec quelque chose comme ceci:

J'espère que vous avez apprécié mon premier article. Si vous l'avez trouvé utile, partagez-le, et si vous voulez rester au courant, vous pouvez me suivre sur Instagram.