Apprenez à créer un carousel d'images simple avec flèches de navigation en HTML, CSS et JavaScript vanilla.
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>
.carousel-container contiendra toutes les slides alignées horizontalement.
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 */
}
transform: translateX() sera utilisée en JavaScript pour faire défiler les slides horizontalement.
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);
%) permet de boucler en continu (revenir au début après la dernière slide).
Voici le carousel en action :
// 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);
}
// 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();
});
// 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
});
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.