Carousel in Vanilla JS

Carousel

🚀 Obiettivo del Progetto

L'obiettivo principale di questo progetto era di non affidarsi a librerie esterne e di realizzare un carousel semplice e leggero.

🏗️ Step di Sviluppo

1. Creazione dell'html

Ho fatto un html molto leggero, ho messo solo lo stretto necessario per poi poter popolare tutto con javascript.

So che avrei potuto mettere tutto in JavaScript, semplicemente non avevo sbatti.🙂
<!doctype html>
<html lang="it-IT">
<head>
  <meta charset="UTF-8" />
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Vanilla Carousel</title>
</head>
<body>
  <div id="app">
    <div class="container">
      <div class="slider"></div>
      <div class="slider-nav">
        <div class="flex">
          <p id="slider-nav-current"></p>
          <div class="slider-nav-divider">
            <div class="slider-nav-divider-progress"></div>
          </div>
          <p id="slider-nav-total"></p>
        </div>
        <div class="">
          <button class="slider-nav-prev"></button>
          <button class="slider-nav-next"></button>
        </div>
      </div>
    </div>
  </div>
  <script type="module" src="/src/main.js"></script>
</body>
</html>

2. Creazione del css

Il css è stato semplice, ho messo semplicemente un pò di stile per arrivare al design finale per poi poter rendere tutto dinamico con javascript.


.slider {
  display: flex;
  justify-content: start;
  overflow-x: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  width: 900px;

  .slider-item-container {
    width: 33.33%;
    height: 280px;
    background-size: cover;
    background-position: center;
    flex-shrink: 0;
    display: flex;
    justify-content: center;
    align-items: center;

    .slider-item {
      width: 97%;
      height: 97%;
      background-size: cover;
      background-position: center;
      border-radius: 10px;
    }
  }
}

.slider-nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  margin-top: 30px;
  padding: 0 5px;
}

NOTA:

Il nesting è una delle ultime funzionalità che sono state introdotte in css, quindi visto che non avevo bisogno di fare chissà cosa, ho usato il css puro.

3. Creazione del javascript

Ok, partiamo con la parte divertente del progetto.
Ho iniziato dichiarando tutte le const che mi servivano e a creare un loop per popolare il carousel con le immagini.


  const slider = document.querySelector('.slider');
  const sliderNavPrev = document.querySelector('.slider-nav-prev');
  const sliderNavNext = document.querySelector('.slider-nav-next');
  const sliderNavCurrent = document.querySelector('#slider-nav-current');
  const sliderNavTotal = document.querySelector('#slider-nav-total');
  const sliderNavDivider = document.querySelector('.slider-nav-divider');
  const sliderNavDividerProgress = document.querySelector('.slider-nav-divider-progress');

  let path_images = [
  '../public/images/1.jpeg',
  '../public/images/2.jpeg',
  '../public/images/3.jpeg',
  '../public/images/4.jpeg',
  '../public/images/5.jpeg',
  '../public/images/4.jpeg',
  '../public/images/3.jpeg',
  '../public/images/1.jpeg',
  '../public/images/5.jpeg',
  '../public/images/3.jpeg',
  '../public/images/1.jpeg',
  '../public/images/5.jpeg',
  '../public/images/3.jpeg',
  '../public/images/1.jpeg',
  '../public/images/5.jpeg',
  ];

  for (let i = 0; i < path_images.length; i++) {
  slider.innerHTML += `
      <div class="slider-item-container">
      <div class="slider-item" style="background-image: url(${path_images[i]})"></div>
      </div>`;
  }
  

3.1. Calcolo delle dimensioni e gestione delle slide

Dopo aver popolato il carosello, ho calcolato un po' di dimensioni chiave per far funzionare tutto a dovere.


  const sliderItemsContainers = document.querySelectorAll('.slider-item-container');
  const singleSlideWidth = sliderItemsContainers[0].clientWidth;

  const totalSlides = sliderItemsContainers.length;
  const slidesPerPage = Math.floor(slider.clientWidth / singleSlideWidth);
  const totalPages = Math.ceil(totalSlides / slidesPerPage);

  let isClick = false;

  sliderNavCurrent.innerHTML = 1;
  sliderNavTotal.innerHTML = totalPages;

  • singleSlideWidth: la larghezza di ogni singola slide.
  • totalSlides: il numero totale di slide presenti.
  • slidesPerPage: quante slide sono visibili contemporaneamente.
  • totalPages: il numero totale di "pagine" del carosello.
  • isClick: serve per prevenire i click multipli che mandavano tutto a farsi benedire. 😅

3.2. Gestione del click con delay

Per evitare che i click multipli sballino i calcoli, ho messo un delay di 800ms tra un click e l’altro.


  sliderNavPrev.addEventListener('click', () => {
    if (isClick) return;
    isClick = true;
    slider.scrollLeft -= slider.clientWidth;
    setTimeout(() => isClick = false, 800);
  });

  sliderNavNext.addEventListener('click', () => {
    if (isClick) return;
    isClick = true;
    slider.scrollLeft += slider.clientWidth;
    setTimeout(() => isClick = false, 800);
  });

3.3. Aggiornamento della progress bar

Infine, ho aggiunto un piccolo sistema per aggiornare la barra di progresso mentre scorri.


  const updateSliderNavDividerProgress = () => {
    const maxScrollLeft = slider.scrollWidth - slider.clientWidth;
    const progress = Math.round((slider.scrollLeft / maxScrollLeft) * 100);
    sliderNavDividerProgress.style.width = `${progress}%`;
  };

  slider.addEventListener('scroll', () => {
    updateSliderNavDividerProgress();
  });

E con questo il carousel è pronto! 🚀

NOTA:

Questo è un progetto che ho trovato su un sito di challenge front-end, mi sembrava interessante provare a realizzare una cosa di cui abitualmente si usano librerie esterne.