Готовый слайдер — отличное решение, пока перед вами типовая задача: три баннера, стрелки, точки. Но как только дизайн требует нестандартного поведения, анимации или логики переключения, а каждый килобайт на счету, собирать компонент вручную становится проще и надёжнее. Разберём, как сделать простой, понятный слайдер на чистом JavaScript — такой, который легко встроить в проект и развивать под свои нужды.
Когда имеет смысл писать слайдер самому
Самописный слайдер — не панацея. Для лендинга с двумя баннерами, где всё работает из коробки, готовый плагин сэкономит время. Но как только появляются специфические требования, свой компонент начинает выигрывать по всем фронтам:
- нужен полный контроль над HTML-разметкой и стилями — никаких компромиссов с чужой структурой;
- важно не тащить лишние зависимости — меньше кода, меньше уязвимостей;
- требуется своя логика переключения, автопрокрутки, свайпа или синхронизации с другими блоками;
- компонент должен легко адаптироваться под дизайн-систему проекта;
- проект должен оставаться понятным для поддержки через несколько месяцев — свой код всегда прозрачнее.
У простого слайдера есть ещё одно важное преимущество: вы понимаете, как он устроен. Это особенно полезно, когда сайт собирается из шаблонов и готовых компонентов, а потом дорабатывается под продуктовые задачи. Вместо того чтобы копаться в документации плагина, вы правите знакомый код.
Из чего состоит простой слайдер
Минимальный слайдер — это не магия, а четыре части:
- обёртка — видимая область, в которой показывается только один слайд;
- трек — элемент, который двигается влево и вправо;
- слайды — отдельные карточки, изображения или блоки контента;
- управление — кнопки, точки, клавиатура, свайп.
Базовая схема почти всегда одна и та же: у трека меняется transform: translateX(...), а активный слайд определяется индексом. Это фундамент, на котором строятся все последующие улучшения.
Базовая структура HTML
Такая разметка проста и читаема. Важно, что логика и внешний вид разделены: HTML отвечает за структуру, CSS — за отображение, JS — за поведение. Это позволяет менять дизайн, не трогая скрипты, и наоборот.
Стили: делаем слайдер без лишней сложности
Базовый CSS
.slider {
position: relative;
width: 100%;
max-width: 800px;
margin: 0 auto;
}
.slider__viewport {
overflow: hidden;
width: 100%;
}
.slider__track {
display: flex;
transition: transform 0.3s ease;
}
.slider__slide {
flex: 0 0 100%;
min-width: 0;
}
.slider__btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: rgba(0,0,0,0.5);
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
}
.slider__btn--prev { left: 10px; }
.slider__btn--next { right: 10px; }
.slider__dots {
display: flex;
justify-content: center;
gap: 8px;
margin-top: 10px;
}
.slider__dot {
width: 10px;
height: 10px;
border-radius: 50%;
border: none;
background: #ccc;
cursor: pointer;
}
.slider__dot--active {
background: #333;
}
Ключевая идея: каждый слайд занимает ровно 100% ширины контейнера. Тогда смещение трека вычисляется элементарно: -100% * индекс. Никаких сложных расчётов, всё прозрачно. flex: 0 0 100% гарантирует, что слайды не сжимаются и не растягиваются, а overflow: hidden отсекает всё лишнее.
JavaScript: логика переключения
Минимальная рабочая версия
const slider = document.querySelector('.slider');
const track = slider.querySelector('.slider__track');
const slides = slider.querySelectorAll('.slider__slide');
const prevBtn = slider.querySelector('.slider__btn--prev');
const nextBtn = slider.querySelector('.slider__btn--next');
const dotsContainer = slider.querySelector('.slider__dots');
let currentIndex = 0;
function updateSlider() {
track.style.transform = `translateX(-${currentIndex * 100}%)`;
const dots = dotsContainer.querySelectorAll('.slider__dot');
dots.forEach((dot, i) => {
dot.classList.toggle('slider__dot--active', i === currentIndex);
});
}
function nextSlide() {
currentIndex = (currentIndex + 1) % slides.length;
updateSlider();
}
function prevSlide() {
currentIndex = (currentIndex - 1 + slides.length) % slides.length;
updateSlider();
}
// Создаём точки
slides.forEach((_, i) => {
const dot = document.createElement('button');
dot.classList.add('slider__dot');
dot.setAttribute('type', 'button');
dot.addEventListener('click', () => {
currentIndex = i;
updateSlider();
});
dotsContainer.appendChild(dot);
});
prevBtn.addEventListener('click', prevSlide);
nextBtn.addEventListener('click', nextSlide);
// Инициализация
updateSlider();
Перед вами уже работающий слайдер: кнопки переключают слайды, точки подсвечиваются, зацикливание работает. Никаких библиотек — только чистый JS. Обратите внимание: точки создаются динамически по количеству слайдов, а значит, разметка остаётся минимальной.
Как это работает по шагам
1. Находим элементы
Сначала скрипт получает обёртку, трек, слайды, кнопки и контейнер точек. Это стандартный подход: вся логика живёт внутри конкретного экземпляра слайдера. Благодаря этому на странице может быть несколько независимых слайдеров — они не будут конфликтовать.
2. Храним текущий индекс
currentIndex — это номер активного слайда. Простая переменная, вокруг которой строится вся логика. Именно она управляет положением трека и состоянием точек.
3. Сдвигаем трек
Формула простая:
track.style.transform = `translateX(-${currentIndex * 100}%)`;
Если активен второй слайд, трек смещается на -100%. Если третий — на -200%. Это работает именно потому, что каждый слайд занимает 100% ширины видимой области.
4. Обновляем точки
Когда слайд меняется, состояние кнопок должно меняться вместе с ним. Иначе пользователь не поймёт, где сейчас находится. Это критично для UX: визуальная обратная связь не должна отставать. Мы просто переключаем класс slider__dot--active у нужной точки.
5. Делаем зацикливание
Операция % slides.length позволяет после последнего слайда перейти к первому. Это удобно для баннеров и промоблоков. Решение простое, но для бесконечной прокрутки с плавным переходом (когда визуально слайды идут по кругу без рывка) потребуется чуть больше кода — но для большинства задач достаточно и такого зацикливания.
Улучшаем слайдер без усложнения кода
Базовый слайдер — это фундамент. Дальше можно добавлять улучшения по мере необходимости, не перегружая код. Вот что чаще всего пригождается в реальных проектах:
| Улучшение | Зачем нужно | Сложность |
|---|---|---|
| Клавиатурная навигация | Удобство для пользователей и доступность | Низкая |
| Свайп на тач-устройствах | Лучше работает на мобильных | Средняя |
| Автопрокрутка | Подходит для промо и баннеров | Низкая |
| Пауза при наведении | Не раздражает пользователя | Низкая |
| Переход к конкретному слайду | Удобно для точек и табов | Низкая |
| Адаптивная высота | Убирает лишние пустоты | Средняя |
Добавляем клавиатуру
Управление с клавиатуры — не просто приятное дополнение, а часть доступности. Если слайдер содержит важный контент, пользователь должен иметь возможность листать его стрелками. Это особенно полезно, когда слайдер участвует в сценарии просмотра, а не просто крутит картинки.
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowLeft') prevSlide();
if (e.key === 'ArrowRight') nextSlide();
});
Лучше вешать обработчик на сам слайдер или его обёртку, чтобы не реагировать на стрелки, когда фокус вне компонента. Но для простоты можно начать с документа, а потом уточнить.
Добавляем автопрокрутку
Автопрокрутка — палка о двух концах. Для баннеров и промо-блоков она работает отлично, но для контента, который пользователь изучает, лучше дать управление в руки человека. В любом случае реализация проста:
let autoplayInterval;
function startAutoplay() {
autoplayInterval = setInterval(nextSlide, 3000);
}
function stopAutoplay() {
clearInterval(autoplayInterval);
}
startAutoplay();
Чтобы не мешать пользователю, лучше ставить паузу при взаимодействии — например, при наведении или фокусе:
slider.addEventListener('mouseenter', stopAutoplay);
slider.addEventListener('mouseleave', startAutoplay);
Так слайдер не будет переключаться, когда человек хочет рассмотреть содержимое.
Что важно для удобства и доступности
Даже простой слайдер должен быть понятным для пользователя. Часто разработчики концентрируются на визуале, забывая о поведении, а потом удивляются, почему компонент неудобен. Вот минимальный чек-лист, который стоит держать в голове:
- у кнопок должны быть понятные подписи (хотя бы aria-label), а не просто стрелки;
- активный слайд должен быть логически выделен — не только точкой, но и, например, фокусом для скринридеров;
- точки должны быть настоящими кнопками (
<button>), а неdiv— иначе они не попадают в фокус и не кликабельны с клавиатуры; - фокус не должен теряться при переключении — если пользователь перешёл кнопкой, фокус может оставаться на ней или перемещаться на новый слайд;
- переключение не должно происходить слишком быстро — анимация в 300–400 мс комфортна, а автопрокрутка быстрее 2–3 секунд раздражает;
- автопрокрутку лучше можно остановить — и обязательно останавливать при любом ручном взаимодействии.
Типовая ошибка
Часто делают слайдер только визуально, забывая про поведение:
- кнопки без
type="button"внутри формы — при нажатии может отправиться форма; - точки без
aria-label— скринридеры молчат; - переключение без синхронизации состояния — трек уже сдвинулся, а точки или индикатор остались в старом состоянии;
- слишком резкая анимация — transition: 0s или мгновенный рывок;
- одинаковая логика для десктопа и мобильного без проверки — на тач-устройствах может не хватать свайпа или, наоборот, кнопки слишком мелкие.
В результате компонент вроде бы работает, но пользоваться им неудобно. Проверяйте эти моменты сразу, а не после жалоб.
Свайп: когда он нужен и как не переусложнить
Свайп нужен в основном на мобильных. Но для простого слайдера его не обязательно делать сразу. Если задача — быстро собрать рабочий компонент, начните с кнопок и точек. Свайп можно добавить позже, когда базовая механика отлажена.
Практичный подход такой:
- сначала делаете переключение по кнопкам — это основа;
- потом добавляете свайп на
pointer-событиях (touchstart, touchmove, touchend) — они универсальнее для мыши и тачпада; - тестируете на реальном телефоне и тачпаде — эмулятор не всегда показывает нюансы;
- проверяете, не ломается ли кликабельность внутри слайда — если слайд содержит кнопки, ссылки или формы, свайп требует особой аккуратности, чтобы не перехватывать обычные клики.
Часто свайп переоценивают: на десктопе он не нужен, а на мобильных кнопки могут быть удобнее, если слайдов мало. Не усложняйте раньше времени.
Когда лучше не писать свой слайдер
Самописный компонент — не универсальный ответ. Иногда готовый плагин действительно лучше. Всё зависит от контекста проекта и команды.
Лучше взять готовое решение, если:
- нужен сложный бесконечный слайдер с нестандартной физикой (например, parallax-эффекты или инерция);
- требуется много жестов и анимаций, которые уже отлажены в библиотеке;
- проект должен быть быстро запущен без разработки компонента — время важнее гибкости;
- у команды нет времени поддерживать свой код — готовый плагин обновляется сообществом;
- нужен большой набор edge-case’ов, уже обработанных библиотекой (например, работа в старых браузерах или нестандартные устройства).
Лучше писать свой слайдер, если:
- нужен компактный и контролируемый код — никакого лишнего;
- важна простота дальнейшей доработки — свой код всегда понятнее;
- слайдер должен подчиняться дизайну проекта, а не наоборот;
- вы хотите понимать каждую часть компонента — это особенно ценно, когда проект растёт;
- проект развивается как продукт, а не как шаблон «поставил и забыл».
Пошаговый план разработки
Ниже — удобная схема, если делать слайдер с нуля. На каждом шаге я добавил типовые грабли, чтобы вы их обошли.
Шаг 1. Сверстать структуру
- обёртка;
- viewport (видимая область);
- track (движущаяся лента);
- slides (слайды);
- buttons (кнопки «вперёд/назад»);
- dots (точки-индикаторы).
Не забудьте type="button" у кнопок, чтобы они не сабмитили форму, если слайдер окажется внутри <form>.
Шаг 2. Написать CSS
- скрыть лишнее через
overflow: hiddenу viewport; - расположить слайды в ряд (flexbox);
- добавить transition для плавности;
- задать адаптивные размеры — слайды должны тянуться по ширине контейнера.
Проверьте, чтобы у слайдов не было лишних отступов, которые ломают расчёт 100%.
Шаг 3. Подключить базовый JS
- найти DOM-элементы;
- завести
currentIndex; - сделать функции
nextSlideиprevSlide; - реализовать
updateSlider.
На этом этапе слайдер уже должен переключаться.
Шаг 4. Добавить точки
- сгенерировать кнопки по количеству слайдов;
- подсвечивать активную;
- сделать переход по клику на точку.
Точки должны быть кнопками, иначе они не работают с клавиатуры.
Шаг 5. Проверить поведение
- переключение туда и обратно;
- зацикливание (последний → первый и наоборот);
- работа на мобильных (ресайз, тач);
- отсутствие ошибок в консоли;
- корректное поведение при одном слайде (кнопки можно скрыть, точки не нужны).
Если слайд один, слайдер вырождается в статичный блок — это нормально, но код не должен падать.
Как тестировать слайдер перед публикацией
Перед тем как ставить компонент в проект, проверьте его на реальных сценариях. Эмуляторы и десктопный браузер не покажут всех проблем. Вот чек-лист, который я использую:
- слайдер открывается без ошибок в консоли;
- кнопки работают в обе стороны, даже если быстро кликать;
- точки совпадают с активным слайдом при любом переходе;
- на мобильном ширина не «прыгает» при смене ориентации;
- контент внутри слайда не ломает вёрстку (длинные слова, большие изображения);
- автопрокрутка не мешает взаимодействию — останавливается при наведении и не запускается, пока пользователь листает вручную;
- компонент не конфликтует с другими скриптами на странице (проверьте, не переопределяются ли глобальные переменные).
Практический вывод
Писать свой простой слайдер вместо готового плагина имеет смысл тогда, когда вам нужен понятный, лёгкий и управляемый компонент. В базовой версии он собирается из трека, слайдов, кнопок и нескольких функций JavaScript. Этого достаточно, чтобы закрыть большинство типовых задач: переключение, точки, зацикливание, автопрокрутку и клавиатурное управление.
Главная идея здесь не в том, чтобы изобрести сложный движок, а в том, чтобы сделать маленький, надёжный и удобный для развития компонент. Именно такой подход хорошо работает в проектах, где шаблон нужно постепенно превращать в живой веб-продукт. Вы не просто получаете слайдер — вы учитесь мыслить компонентами и понимать, как устроены даже более сложные интерфейсы.
FAQ
Можно ли сделать слайдер вообще без JavaScript?
Формально да, но это будет не интерактивный слайдер, а скорее вёрстка с CSS-переключением (например, через :target или radio-кнопки). Для нормального управления — кнопки, точки, клавиатура — JavaScript необходим. Без него вы теряете гибкость и доступность.
Что проще: translateX или scroll-snap?
Для базового слайдера проще translateX. Вы полностью контролируете смещение и анимацию. scroll-snap удобен для свайповых интерфейсов, но управлять логикой (точки, зацикливание, программный переход) там сложнее — приходится работать с положением прокрутки, а не с индексом.
Сколько слайдов можно добавить?
С технической точки зрения — сколько угодно, но на практике лучше держать слайдер коротким. Если контента слишком много, пользователь быстрее устаёт, а производительность может упасть (особенно если внутри слайдов тяжёлые изображения). Для длинных последовательностей лучше подумать о другом паттерне — например, сетке или ленивой подгрузке.
Нужна ли библиотека для простого слайдера?
Нет, если задача типовая. Для базового переключения библиотека часто не нужна — вы только добавите лишний вес и зависимость. Библиотеки вроде Swiper или Splide имеет смысл подключать, когда нужны сложные эффекты, виртуальные слайды или тонкая настройка жестов.
Какой самый частый баг в самописном слайдере?
Чаще всего забывают синхронизировать состояние: трек уже сдвинулся, а точки, кнопки или индикатор остались в старом состоянии. Это сбивает пользователя. Решается просто: всегда обновлять всё в одной функции (updateSlider), которая вызывается при любом изменении индекса.