Практика: карусель изображений
Free PreviewПришло время применить всё, что мы узнали о событиях и манипуляции DOM. Создадим карусель изображений — компонент, который автоматически листает фотографии Санкт-Петербурга и позволяет управлять им вручную.
Что будем делать
Карусель будет:
- автоматически менять слайды каждые 3 секунды
- реагировать на кнопки «вперёд» и «назад»
- показывать точки-индикаторы текущего слайда
- останавливать автоматическую прокрутку при ручном управлении
HTML-структура
Добавьте в разметку страницы блок карусели — например, после секции hero:
<section class="carousel-section"> <div class="carousel"> <div class="carousel__track"> <div class="carousel__slide active"> <img src="https://s3.twcstorage.ru/f280a6ef-learning-hub-test/lessons/frontend-basics/assets/spb-hermitage.jpg" alt="Эрмитаж"> <p class="carousel__caption">Эрмитаж — один из крупнейших музеев мира</p> </div> <div class="carousel__slide"> <img src="https://s3.twcstorage.ru/f280a6ef-learning-hub-test/lessons/frontend-basics/assets/spb-peterhof.jpg" alt="Петергоф"> <p class="carousel__caption">Петергоф — дворцово-парковый ансамбль на берегу Финского залива</p> </div> <div class="carousel__slide"> <img src="https://s3.twcstorage.ru/f280a6ef-learning-hub-test/lessons/frontend-basics/assets/spb-nevsky.jpg" alt="Невский проспект"> <p class="carousel__caption">Невский проспект — главная улица города</p> </div> <div class="carousel__slide"> <img src="https://s3.twcstorage.ru/f280a6ef-learning-hub-test/lessons/frontend-basics/assets/spb-isaakiy.jpg" alt="Исаакиевский собор"> <p class="carousel__caption">Исаакиевский собор — символ имперского Петербурга</p> </div> </div> <button class="carousel__btn carousel__btn--prev" aria-label="Предыдущий слайд">←</button> <button class="carousel__btn carousel__btn--next" aria-label="Следующий слайд">→</button> <div class="carousel__dots"></div> </div> </section>html
CSS
Идея карусели простая: все слайды лежат в одном контейнере, но видно только тот, у которого есть класс active. Остальные скрыты через opacity и position: absolute — это даёт плавный переход без прыжков:
.carousel-section { padding: 60px 20px; background: #f8f8f8; } .carousel { position: relative; max-width: 800px; margin: 0 auto; overflow: hidden; border-radius: 12px; } .carousel__track { position: relative; height: 450px; } .carousel__slide { position: absolute; inset: 0; opacity: 0; transition: opacity 0.5s ease; pointer-events: none; } .carousel__slide.active { opacity: 1; pointer-events: auto; } .carousel__slide img { width: 100%; height: 100%; object-fit: cover; display: block; } .carousel__caption { position: absolute; bottom: 0; left: 0; right: 0; padding: 16px 20px; background: linear-gradient(transparent, rgba(0, 0, 0, 0.6)); color: #fff; margin: 0; } .carousel__btn { position: absolute; top: 50%; transform: translateY(-50%); background: rgba(255, 255, 255, 0.85); border: none; border-radius: 50%; width: 44px; height: 44px; font-size: 18px; cursor: pointer; transition: background 0.2s ease; z-index: 1; } .carousel__btn:hover { background: #fff; } .carousel__btn--prev { left: 12px; } .carousel__btn--next { right: 12px; } .carousel__dots { position: absolute; bottom: 12px; left: 50%; transform: translateX(-50%); display: flex; gap: 8px; z-index: 1; } .carousel__dot { width: 8px; height: 8px; border-radius: 50%; background: rgba(255, 255, 255, 0.5); cursor: pointer; transition: background 0.2s ease; border: none; padding: 0; } .carousel__dot.active { background: #fff; }css
JavaScript
Теперь самое интересное. Создайте отдельный файл carousel.js и подключите его к странице с атрибутом defer:
<script src="carousel.js" defer></script>html
Логика карусели:
const slides = document.querySelectorAll('.carousel__slide'); const dotsContainer = document.querySelector('.carousel__dots'); const prevBtn = document.querySelector('.carousel__btn--prev'); const nextBtn = document.querySelector('.carousel__btn--next'); let current = 0; let autoplayTimer = null; // --- Создаём точки-индикаторы --- slides.forEach((_, i) => { const dot = document.createElement('button'); dot.classList.add('carousel__dot'); dot.setAttribute('aria-label', `Слайд ${i + 1}`); if (i === 0) dot.classList.add('active'); dotsContainer.append(dot); }); const dots = document.querySelectorAll('.carousel__dot'); // --- Переключение слайда --- function goTo(index) { slides[current].classList.remove('active'); dots[current].classList.remove('active'); current = (index + slides.length) % slides.length; // зацикливаем slides[current].classList.add('active'); dots[current].classList.add('active'); } // --- Автовоспроизведение --- function startAutoplay() { autoplayTimer = setInterval(() => { goTo(current + 1); }, 3000); } function stopAutoplay() { clearInterval(autoplayTimer); } startAutoplay(); // --- Кнопки управления --- prevBtn.addEventListener('click', () => { stopAutoplay(); goTo(current - 1); }); nextBtn.addEventListener('click', () => { stopAutoplay(); goTo(current + 1); }); // --- Клик по точкам через делегирование --- dotsContainer.addEventListener('click', (e) => { const dot = e.target.closest('.carousel__dot'); if (!dot) return; stopAutoplay(); const index = Array.from(dots).indexOf(dot); goTo(index); }); // --- Навигация с клавиатуры --- document.addEventListener('keydown', (e) => { if (e.key === 'ArrowLeft') { stopAutoplay(); goTo(current - 1); } if (e.key === 'ArrowRight') { stopAutoplay(); goTo(current + 1); } });js
Разберём несколько ключевых моментов.
(index + slides.length) % slides.length — это трюк для зацикливания. Когда index равен -1 (ушли левее первого слайда), формула даёт slides.length - 1 — то есть последний слайд. Когда index равен slides.length — получаем 0, первый слайд.
stopAutoplay при ручном управлении — хорошая практика: если пользователь сам нажал кнопку, автоматическая прокрутка останавливается. Это не раздражает и даёт ощущение контроля. Можно доработать: возобновлять автовоспроизведение через несколько секунд после последнего взаимодействия.
Делегирование для точек — вместо того чтобы вешать обработчик на каждую точку, мы поставили один обработчик на контейнер. Как мы разбирали в предыдущем уроке.
Клавиатурная навигация — дополнительный штрих. Стрелки влево/вправо переключают слайды без мыши. Это и удобно, и хорошая практика с точки зрения доступности.
Ожидаемый результат
После выполнения задания на странице должна появиться карусель, которая:
- показывает слайды с фотографиями Петербурга
- автоматически переключает их каждые 3 секунды
- переключается вперёд и назад по кнопкам
- подсвечивает активную точку-индикатор
- останавливает автопрокрутку при ручном управлении
- реагирует на стрелки клавиатуры
Если хочется усложнить задачу — попробуйте добавить возобновление автовоспроизведения через 5 секунд после последнего взаимодействия с кнопками. Для этого понадобится setTimeout после каждого вызова stopAutoplay.