← Retour à l'accueil

Tutoriel : Carousel d'Images

Apprenez à créer un carousel d'images simple avec flèches de navigation en HTML, CSS et JavaScript vanilla.

Étape 1 Structure HTML

Commençons par créer la structure de base du carousel :

<!-- Conteneur principal du carousel -->
<div class="carousel">
    <!-- Conteneur qui va glisser horizontalement -->
    <div class="carousel-container">
        <!-- Chaque slide contient une image -->
        <div class="carousel-slide">
            <img src="image1.jpg" alt="Image 1">
        </div>
        <div class="carousel-slide">
            <img src="image2.jpg" alt="Image 2">
        </div>
        <div class="carousel-slide">
            <img src="image3.jpg" alt="Image 3">
        </div>
        <div class="carousel-slide">
            <img src="image4.jpg" alt="Image 4">
        </div>
    </div>

    <!-- Boutons de navigation (flèches gauche/droite) -->
    <button class="carousel-btn prev" aria-label="Image précédente">❮</button>
    <button class="carousel-btn next" aria-label="Image suivante">❯</button>

    <!-- Conteneur pour les petits points indicateurs (sera rempli en JavaScript) -->
    <div class="carousel-indicators"></div>
</div>
💡 Point clé : On utilise une structure imbriquée pour contrôler le défilement. Le .carousel-container contiendra toutes les slides alignées horizontalement.

Étape 2 Styles CSS

Ajoutons les styles pour positionner et masquer les slides :

/* Conteneur principal du carousel */
.carousel {
    position: relative;        /* Permet de positionner les boutons en absolu à l'intérieur */
    width: 100%;              /* Prend toute la largeur disponible */
    max-width: 800px;         /* Limite la largeur maximale */
    margin: 0 auto;           /* Centre le carousel horizontalement */
    overflow: hidden;         /* Cache les slides qui dépassent */
    border-radius: 15px;      /* Arrondit les coins */
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);  /* Ombre portée */
}

/* Conteneur qui contient toutes les slides */
.carousel-container {
    display: flex;            /* Aligne les slides horizontalement */
    transition: transform 0.5s ease-in-out;  /* Animation fluide lors du défilement */
}

/* Chaque slide individuelle */
.carousel-slide {
    min-width: 100%;          /* Chaque slide prend 100% de la largeur du carousel */
    height: 500px;            /* Hauteur fixe pour toutes les slides */
}

/* Images dans les slides */
.carousel-slide img {
    width: 100%;              /* L'image prend toute la largeur */
    height: 100%;             /* L'image prend toute la hauteur */
    object-fit: cover;        /* Recadre l'image pour remplir l'espace */
    display: block;           /* Enlève l'espace en bas de l'image */
}

/* ===== Boutons de navigation ===== */
.carousel-btn {
    position: absolute;       /* Positionné au-dessus du carousel */
    top: 50%;                 /* Centré verticalement */
    transform: translateY(-50%);  /* Ajustement pour centrage parfait */
    background: rgba(0, 0, 0, 0.5);  /* Fond noir semi-transparent */
    color: white;             /* Texte blanc */
    border: none;             /* Pas de bordure */
    padding: 15px 20px;       /* Espacement interne */
    font-size: 24px;          /* Taille de la flèche */
    cursor: pointer;          /* Curseur en forme de main */
    transition: all 0.3s ease;  /* Animation au survol */
    z-index: 10;              /* Place les boutons au-dessus des images */
}

/* Effet au survol des boutons */
.carousel-btn:hover {
    background: rgba(0, 0, 0, 0.8);  /* Fond plus foncé au survol */
}

/* Bouton précédent (positionné à gauche) */
.carousel-btn.prev {
    left: 20px;               /* 20px du bord gauche */
}

/* Bouton suivant (positionné à droite) */
.carousel-btn.next {
    right: 20px;              /* 20px du bord droit */
}

/* ===== Indicateurs (petits points en bas) ===== */
.carousel-indicators {
    position: absolute;       /* Positionné au-dessus du carousel */
    bottom: 20px;             /* 20px du bas */
    left: 50%;                /* Centré horizontalement */
    transform: translateX(-50%);  /* Ajustement pour centrage parfait */
    display: flex;            /* Aligne les points horizontalement */
    gap: 10px;                /* Espace entre les points */
}

/* Chaque point indicateur */
.indicator {
    width: 12px;              /* Largeur du point */
    height: 12px;             /* Hauteur du point */
    border-radius: 50%;       /* Forme ronde */
    background: rgba(255, 255, 255, 0.5);  /* Blanc semi-transparent */
    cursor: pointer;          /* Curseur en forme de main */
    transition: all 0.3s ease;  /* Animation */
}

/* Point indicateur actif (slide courante) */
.indicator.active {
    background: white;        /* Blanc opaque */
    transform: scale(1.2);    /* Légèrement plus grand */
}
💡 Point clé : La propriété transform: translateX() sera utilisée en JavaScript pour faire défiler les slides horizontalement.

Étape 3 JavaScript

Maintenant, ajoutons l'interactivité :

// Classe qui gère tout le fonctionnement du carousel
class Carousel {
    // Le constructeur s'exécute quand on crée un nouveau carousel
    constructor(element) {
        // On stocke l'élément HTML du carousel
        this.carousel = element;
        // On récupère le conteneur qui va glisser
        this.container = element.querySelector('.carousel-container');
        // On récupère toutes les slides (images)
        this.slides = element.querySelectorAll('.carousel-slide');
        // On récupère les boutons précédent et suivant
        this.prevBtn = element.querySelector('.prev');
        this.nextBtn = element.querySelector('.next');
        // On récupère le conteneur des indicateurs
        this.indicatorsContainer = element.querySelector('.carousel-indicators');

        // Index de la slide actuellement affichée (on commence à 0)
        this.currentIndex = 0;
        // Nombre total de slides
        this.totalSlides = this.slides.length;

        // On lance l'initialisation
        this.init();
    }

    // Méthode d'initialisation appelée au démarrage
    init() {
        // On crée les petits points indicateurs
        this.createIndicators();
        // On affiche la première slide
        this.updateCarousel();

        // On écoute les clics sur le bouton "précédent"
        this.prevBtn.addEventListener('click', () => this.prevSlide());
        // On écoute les clics sur le bouton "suivant"
        this.nextBtn.addEventListener('click', () => this.nextSlide());

        // Navigation avec les flèches du clavier
        document.addEventListener('keydown', (e) => {
            if (e.key === 'ArrowLeft') this.prevSlide();   // Flèche gauche
            if (e.key === 'ArrowRight') this.nextSlide();  // Flèche droite
        });
    }

    // Crée les petits points indicateurs en bas du carousel
    createIndicators() {
        // On boucle sur chaque slide pour créer un point
        for (let i = 0; i < this.totalSlides; i++) {
            // On crée un élément div pour le point
            const indicator = document.createElement('div');
            // On lui ajoute la classe "indicator"
            indicator.classList.add('indicator');
            // Le premier point est actif par défaut
            if (i === 0) indicator.classList.add('active');

            // Quand on clique sur un point, on va à la slide correspondante
            indicator.addEventListener('click', () => this.goToSlide(i));
            // On ajoute le point au conteneur d'indicateurs
            this.indicatorsContainer.appendChild(indicator);
        }
        // On récupère tous les indicateurs créés
        this.indicators = this.indicatorsContainer.querySelectorAll('.indicator');
    }

    // Met à jour l'affichage du carousel
    updateCarousel() {
        // On calcule le décalage en pourcentage
        // Ex: slide 0 = 0%, slide 1 = -100%, slide 2 = -200%, etc.
        const offset = -this.currentIndex * 100;
        // On applique la translation pour faire glisser le conteneur
        this.container.style.transform = `translateX(${offset}%)`;

        // On met à jour les indicateurs
        this.indicators.forEach((indicator, index) => {
            // On ajoute/retire la classe "active" selon l'index
            indicator.classList.toggle('active', index === this.currentIndex);
        });
    }

    // Aller à la slide suivante
    nextSlide() {
        // L'opérateur % (modulo) permet de revenir à 0 après la dernière slide
        // Ex: si on est à la slide 3 sur 4 slides, (3 + 1) % 4 = 0
        this.currentIndex = (this.currentIndex + 1) % this.totalSlides;
        // On met à jour l'affichage
        this.updateCarousel();
    }

    // Aller à la slide précédente
    prevSlide() {
        // On ajoute totalSlides avant le modulo pour éviter les nombres négatifs
        // Ex: si on est à la slide 0, (0 - 1 + 4) % 4 = 3 (dernière slide)
        this.currentIndex = (this.currentIndex - 1 + this.totalSlides) % this.totalSlides;
        // On met à jour l'affichage
        this.updateCarousel();
    }

    // Aller directement à une slide spécifique
    goToSlide(index) {
        // On change l'index courant
        this.currentIndex = index;
        // On met à jour l'affichage
        this.updateCarousel();
    }
}

// ===== Initialisation du carousel =====
// On sélectionne l'élément HTML avec la classe "carousel"
const carouselElement = document.querySelector('.carousel');
// On crée une nouvelle instance de Carousel
const carousel = new Carousel(carouselElement);
💡 Point clé : L'opérateur modulo (%) permet de boucler en continu (revenir au début après la dernière slide).

Démo Résultat final

Voici le carousel en action :

Bonus Améliorations possibles

🔄 Défilement automatique

// Démarre le défilement automatique (par défaut toutes les 3 secondes)
startAutoplay(interval = 3000) {
    // setInterval exécute nextSlide() à intervalle régulier
    this.autoplayInterval = setInterval(() => {
        this.nextSlide();
    }, interval);
}

// Arrête le défilement automatique
stopAutoplay() {
    // On efface l'intervalle pour stopper l'autoplay
    clearInterval(this.autoplayInterval);
}

👆 Swipe tactile

// Variables pour stocker la position du doigt
let touchStartX = 0;  // Position de départ
let touchEndX = 0;    // Position de fin

// Quand l'utilisateur pose son doigt sur l'écran
carousel.addEventListener('touchstart', (e) => {
    touchStartX = e.changedTouches[0].screenX;
});

// Quand l'utilisateur lève son doigt
carousel.addEventListener('touchend', (e) => {
    touchEndX = e.changedTouches[0].screenX;
    // Si on a glissé vers la gauche (> 50px)
    if (touchStartX - touchEndX > 50) nextSlide();
    // Si on a glissé vers la droite (> 50px)
    if (touchEndX - touchStartX > 50) prevSlide();
});

⏸️ Pause au survol

// Quand la souris entre dans le carousel
carousel.addEventListener('mouseenter', () => {
    this.stopAutoplay();  // On met en pause
});

// Quand la souris sort du carousel
carousel.addEventListener('mouseleave', () => {
    this.startAutoplay(); // On relance l'autoplay
});

♾️ Boucle infinie

Pour un défilement infini sans retour au début, il faut cloner les premières et dernières slides et ajuster la logique de navigation.