Skip to content
yanaleksandrovPublic

About

✨ A lightweight (~17kB minified) library to create input range sliders with one or two drag handles

Resources

Stars

0 stars

Watchers

1 watching

Forks

Latest commit

 

History

37 Commits

Folders and files

Repository files navigation

💥 О библиотеке

Современная лёгкая (~7 КБ) JS-библиотека без зависимостей для создания слайдеров диапазона с одной или двумя ручками перетаскивания.

alt text

👍 Возможности

  • Глубокая настройка через CSS
  • Плоский скин Coral по умолчанию, готовый Classic-скин и собственные скины через CSS-переменные, с динамическим переключением
  • Поддержка сенсорного управления (touch)
  • Поддержка отрицательных и дробных значений
  • Отсутствие зависимостей
  • Поддержка всеми основными браузерами

✨ Использование

Добавьте <input type="range"> — класс на нём не нужен, вся базовая стилизация (включая ту, что предотвращает «прыжок» разметки при инициализации, см. ниже) завязана на сам селектор input[type="range"], а не на класс. Чтобы получить слайдер диапазона (две ручки), добавьте атрибут data-points с начальным значением верхней ручки — иначе будет одна ручка. Значение — по сути список через запятую, задел на будущее для нескольких дополнительных ручек, но сегодня используется только первое число (максимум две ручки на слайдер).

<!-- одна ручка -->
<input type="range" min="0" max="100" step="1" value="40" />

<!-- две ручки -->
<input type="range" min="0" max="100" step="1" value="20" data-points="80" />

Инициализируйте библиотеку на каждом слайдере (target может быть CSS-селектором или самим элементом):

document.querySelectorAll('input[type="range"]').forEach((slider) => {
  new Ranger(slider, {
    scaleTicksCount: 10,
    scaleTickPrefix: '',
    scaleTickSuffix: '',
    labelIsVisible: true,
    labelPrefix: '',
    labelSuffix: '',
    disabled: false,
  });
});

Ranger всё равно добавляет класс ranger-input на сам <input> при инициализации (настраивается через classes.input) — как стабильный хук для собственных CSS-селекторов или querySelectorAll, но для самой стилизации он не требуется.

✨ API

Опция Тип По умолчанию Описание
classes object { container: 'ranger', input: 'ranger-input', fill: 'ranger-fill', inputTo: 'ranger-input--to', scale: 'ranger-scale', scaleTick: 'ranger-scale-tick', scaleMinorTick: 'ranger-scale-tick ranger-scale-tick--minor', scaleTickLimit: 'ranger-scale-tick--limit', label: 'ranger-label', labelItem: 'ranger-label-item' } Имена классов для каждого элемента, создаваемого Ranger. input добавляется на сам <input> при инициализации как удобный хук — сама стилизация от него не зависит (см. «Использование» выше), в CSS <input> выбирается селектором input[type="range"]. scaleTickLimit добавляется поверх обычного класса деления (не заменяет его) — см. minValue/maxValue.
scaleTicksCount number 10 Количество подписанных (крупных) делений шкалы. 0 — полностью скрыть шкалу.
scaleMinorTicksCount number 0 Непомеченные мелкие деления между каждой парой крупных, равномерно делящие промежуток.
scaleAnimatedTicksCount number 1 Радиус (в делениях) с каждой стороны от ручки, на котором живёт дуга --ranger-scale (см. ниже): 1 (максимум) прямо у ручки, плавный (не линейный) спад к 0 на дальнем краю этого радиуса. Один общий радиус, применяется симметрично.
scaleTickPrefix string '' Текст перед значением каждого деления.
scaleTickSuffix string '' Текст после значения каждого деления.
labelIsVisible boolean true Показать/скрыть плавающую подпись(и) значения над ручкой(ами).
labelPrefix string '' Текст перед значением подписи.
labelSuffix string '' Текст после значения подписи.
labelOnDragOnly boolean false Показывать подпись, только пока ручка активно перетаскивается или перемещается клавиатурой.
disabled boolean false Отключить перетаскивание слайдера.
fromInput HTMLInputElement null Внешний <input>, синхронизируемый с нижней/единственной ручкой.
toInput HTMLInputElement null Внешний <input>, синхронизируемый с верхней ручкой (только режим диапазона).
values array null Отображаемые значения (подписи, даты, ценовые уровни...) для выбора по индексу — min/max/step вычисляются автоматически, а scaleTicksCount по умолчанию равен одному делению на значение (оба — если явно не переопределены). Задаёт format по умолчанию, если он не указан.
format (value) => string null Переопределяет labelPrefix/labelSuffix и scaleTickPrefix/scaleTickSuffix везде, где отображается значение. Возвращённая строка (как и labelPrefix/labelSuffix) рендерится во всплывающей подписи как HTML, а не как экранированный текст — можно вернуть разметку (иконку, <strong> и т.п.).
logScale boolean false Отображает/форматирует значение экспоненциально между min и max (должно быть > 0); перетаскивание остаётся линейным. Задаёт format по умолчанию, если он не указан. Начальные value/data-points в разметке задаются в реальных единицах (например, цена $10) — Ranger сам переводит их в линейную позицию при инициализации, вычислять её вручную не нужно.
minValue number null Ограничивает результат каждого перетаскивания/навигации снизу, не трогая нативные min/max — шкала, деления и подпись продолжают охватывать весь диапазон (например, целый месяц values), а выбираемым остаётся только более узкое окно внутри него. Деление шкалы, совпавшее с этим значением, получает класс scaleTickLimit и остаётся видимым целиком, даже если адаптивное прореживание скрыло бы его.
maxValue number null То же самое сверху — вместе с minValue ограничивает выбор диапазоном [minValue, maxValue] внутри полной шкалы, с тем же выделением деления.
snapPoints number[] [] Значения, магнитно притягивающие ручку при приближении в пределах snapThreshold.
snapThreshold number 0.02 Радиус притяжения как доля диапазона min–max.
fineStep number null Шаг для точной навигации Shift+стрелка; по умолчанию step / 10.
minGap number 0 Минимальное расстояние между двумя ручками (только режим диапазона). Игнорируется, если задан fixedRange.
fixedRange boolean | number false Только режим диапазона. Фиксирует расстояние между ручками: за какую ни потяни, обе двигаются вместе, сохраняя ширину диапазона — например, часовой слот записи на приём, который можно перенести на другое время, но нельзя растянуть или сжать. true фиксирует то расстояние, с которым слайдер смонтирован (data-points минус value); число задаёт конкретную ширину явно.
fillGradient string null CSS background (обычно linear-gradient), закрашивающий заливку вместо обычного accent-color — например, зелёный→жёлтый→красный для индикатора риска/температуры. Растягивается по собственному прямоугольнику заливки, поэтому в режиме диапазона охватывает выбранный поддиапазон, а не фиксированную позицию на всей дорожке.
marks array [] Фиксированные точки/зоны на дорожке, независимые от шкалы делений: числа или { value, label?, className? } для точки, { from, to, label?, className? } — для зоны (например, «комфортный диапазон» рядом с отметкой рекомендованного значения).

🔔 Callback

Callback Когда вызывается
onStart начало drag
onChange изменение значения
onEnd окончание drag
onFocus получение фокуса
onBlur потеря фокуса

Каждый колбэк получает (value, slider): value — это текущее значение (или [from, to] в режиме диапазона), а slider — конкретная ручка (<input>), вызвавшая событие. При перетаскивании самой заливки (см. ниже) slider — это элемент заливки, а value — пара [from, to].

new Ranger('#price', {
  onStart: (value, slider) => console.log('начало:', value),
  onChange: (value, slider) => console.log('изменение:', value),
  onEnd: (value, slider) => console.log('окончание:', value),
  onFocus: (value, slider) => console.log('фокус:', value),
  onBlur: (value, slider) => console.log('потеря фокуса:', value),
});

Некоторые взаимодействия работают автоматически, без отдельной опции для включения:

  • Двойной клик/тап по ручке возвращает её к значению, с которого она начиналась.
  • Перетаскивание самой заливки (только режим диапазона) сдвигает обе ручки вместе, сохраняя расстояние между ними — как перемещение целого окна диапазона дат вместо изменения его размера.
  • Стрелки на сфокусированной ручке сдвигают её на step, а с зажатым Shift — на fineStep. Home/End/PageUp/PageDown по-прежнему обрабатываются самим браузером.

Каждое деление — крупное или мелкое — также несёт живое CSS-свойство --ranger-scale (0–1) — дугу с пиком 1 прямо у ближайшей ручки, плавно (не линейно) спадающую к 0 на дальнем краю радиуса scaleAnimatedTicksCount, — чтобы обычный CSS мог его анимировать. .ranger-scale-tick--animated поставляется с библиотекой как готовый пример этого — добавьте его через classes.scaleTick/classes.scaleMinorTick, чтобы деление увеличивалось в высоту и проявлялось по непрозрачности при прохождении ручки мимо:

new Ranger(slider, {
  classes: {
    scaleTick: 'ranger-scale-tick ranger-scale-tick--animated',
    scaleMinorTick: 'ranger-scale-tick ranger-scale-tick--minor ranger-scale-tick--animated',
  },
});

Либо напишите собственную реакцию на --ranger-scale:

.ranger-scale-tick::before {
  height: calc(4px + var(--ranger-scale, 0) * 12px);
  transition: height 0.15s ease-out;
}

Крупные деления — сама чёрточка и подпись вместе — также автоматически прореживаются — при каждом изменении отображаемой ширины слайдера (отслеживается через ResizeObserver, а не только при загрузке страницы) измеряется самая широкая подпись, и скрывается каждое N-е деление целиком, ровно настолько, чтобы ни одна подпись не накладывалась на другую.

🔧 Методы

Метод Описание
setValue(value) Программно перемещает нижнюю/единственную ручку — через тот же конвейер, что и обычное взаимодействие (step/snapPoints/minValue/maxValue, заливка, подпись, шкала, onChange, внешние поля).
setRange(from, to) То же самое для обеих ручек сразу (только режим диапазона; иначе выбрасывает исключение). Оба значения применяются до того, как сработает конвейер, поэтому minGap/fixedRange видят настоящую целевую пару, а не устаревшую.
update(options) Применяет часть опций после монтирования — min/max/step/disabled/snapPoints/minValue/maxValue/minGap/fineStep/fixedRange/format/fillGradient/labelIsVisible/marks и другие. Шкала и метки перестраиваются, если изменилось что-то, из чего строится их DOM (min/max/values/scaleTicksCount/scaleMinorTicksCount/format/marks); подпись создаётся или удаляется при переключении labelIsVisible.
const ranger = new Ranger('#price', { min: 0, max: 1000 });

ranger.setValue(250);
// ranger.setRange(100, 400); — только для диапазонного слайдера

ranger.update({ max: 5000, disabled: false });

📍 Метки

marks добавляет фиксированные точки на дорожку независимо от шкалы делений scaleTicksCount — удобно для отметки рекомендованного значения, прошлого среднего или любой другой опорной точки, которая не должна зависеть от количества и шага обычных делений:

new Ranger('#price', {
  marks: [
    500,
    { value: 750, label: 'Рекомендовано', className: 'ranger-mark--highlight' },
    { from: 200, to: 400, label: 'Комфортная зона' },
  ],
});

Точечная метка (value) — элемент .ranger-mark (класс настраивается через classes.markItem). Метка-зона (from/to) — тот же элемент, но с классом classes.markRange (по умолчанию ещё и с .ranger-mark--range) и шириной, растянутой на весь диапазон from–to, а не точкой. Обе используют --ranger-mark-color как цвет по умолчанию и необязательную подпись label внутри <ins>.

Метки позиционируются в процентах — так же, как заливка: дорожка каждого встроенного скина растянута на всю ширину без отступа под ручку (см. отрицательный отступ ::-webkit-slider-runnable-track/ ::-moz-range-track в разделе «Скины» ниже), поэтому дополнительная поправка на ширину ручки метке не нужна.

🎨 Скины

Все размеры и цвета слайдера — высота дорожки, скругление углов, цвет заливки/акцента, размеры и форма ручки, деления шкалы — заданы CSS-переменными, а не жёстко закодированными значениями, поэтому внешний вид можно полностью переопределить, вплоть до другого силуэта, а не только палитры:

Переменная По умолчанию (скин Coral) Назначение
--ranger-height 6px Высота дорожки.
--ranger-radius 0 Скругление углов дорожки и заливки.
--ranger-gap-top 2.5rem Отступ над дорожкой, зарезервированный под всплывающую подпись.
--ranger-gap-bottom 4rem Отступ под дорожкой, зарезервированный под шкалу делений.
--ranger-bg #e2e2e2 Фон дорожки.
--ranger-accent #ff6f59 Цвет заливки, accent-color, обводка фокуса, подсветка анимированных делений.
--ranger-thumb-width 1.25rem Ширина ручки.
--ranger-thumb-height 2rem Высота ручки.
--ranger-thumb-radius 0 Скругление углов ручки.
--ranger-thumb-bg transparent Фон ручки.
--ranger-thumb-hover-bg transparent Фон ручки при наведении.
--ranger-thumb-border transparent Рамка ручки.
--ranger-thumb-icon initial (нет) Иконка на ручке. В скине Coral сама полоска-ручка рисуется через mask на псевдоэлементе, а не через эту переменную — она используется по назначению в скине Classic (см. ниже).
--ranger-thumb-shadow none box-shadow ручки.
--ranger-tick-color #bdbdbd Цвет линии деления.
--ranger-tick-label-color #888 Цвет подписи деления.
--ranger-mark-color #f59e0b Цвет метки по умолчанию (marks), если у неё нет своего className.

Скин Coral — плоский серо-коралловый стиль в духе скинов Ion.RangeSlider: прямые углы вместо скруглённых и тонкая полоска-ручка вместо квадратной с иконкой — применяется по умолчанию, без каких-либо атрибутов.

Готовый скин Classic — прежний внешний вид: скруглённая дорожка и квадратная ручка с иконкой- грипсами. Включается атрибутом data-skin="classic" — на самом слайдере или на любом его родителе (например, на <html>, чтобы разом переодеть все слайдеры на странице):

<div data-skin="classic">
  <input type="range" min="0" max="100" step="1" value="30" />
</div>

Поскольку это чистые CSS-переменные, уже смонтированный слайдер переодевается сразу, как только атрибут меняется — без повторной инициализации. Исключение — сама заливка: её цвет читается через getComputedStyle(...).accentColor один раз на каждое изменение значения (см. fillSlider в index.js), а не отслеживается непрерывно. Чтобы обновить уже отрисованные слайдеры мгновенно (не дожидаясь следующего перетаскивания), достаточно вызвать fillSlider() у каждого экземпляра — все смонтированные инстансы доступны через Ranger.instances:

document.getElementById('skin-switcher').addEventListener('change', (event) => {
  document.documentElement.dataset.skin = event.target.value; // '', 'classic', ...

  Ranger.instances.forEach((instance) => instance.fillSlider());
});

Все встроенные скины разделяют один и тот же приём: ::-webkit-slider-runnable-track/ ::-moz-range-track получают отрицательный margin-inline, равный половине --ranger-thumb-width, что отменяет стандартное смещение центра ручки от края дорожки — благодаря этому ручка при любом скине доходит до самого края дорожки, а marks и заливка совпадают с ней по проценту без какой-либо ручной настройки.

Свой скин описывается точно так же — своим набором переопределений под собственным атрибутом или классом.

♿ Доступность

Каждая ручка — это обычный нативный <input type="range">, поэтому она изначально фокусируется по Tab, читается скринридерами как ползунок (role="slider") и управляется стрелками/Home/End/PageUp/ PageDown без какого-либо кода со стороны Ranger. Библиотека дополняет это следующим:

  • aria-valuetext — обновляется на каждое изменение значения и озвучивает то же самое, что видно на экране (сопоставленный элемент values, значение logScale, format, labelPrefix/labelSuffix), а не «сырое» число или индекс, которое иначе прочитал бы скринридер.
  • aria-hidden="true" — на заливке, шкале делений и всплывающей подписи: это чисто визуальные дубликаты значения, уже доступного через саму ручку, и без этого атрибута скринридер озвучивал бы каждое значение по два-три раза.
  • Видимый фокус — :focus-visible рисует чёткую обводку вокруг ручки для клавиатурной навигации, так как кастомные ::-webkit-slider-thumb/::-moz-range-thumb не всегда получают стандартную обводку фокуса браузера.

🔄 Поддержка RTL

Направление наследуется через CSS от dir="rtl" (на самом слайдере или любом родителе) — отдельная опция не нужна. Как только направление меняется, Ranger автоматически отражает:

  • заливку и деления шкалы (через логические CSS-свойства inset-inline-*, а не left/right);
  • позицию всплывающей подписи;
  • клик по дорожке (positionToValue) и перетаскивание всего диапазона за заливку — оба учитывают направление, чтобы «вправо» и «влево» соответствовали ожидаемому визуальному смыслу.
<div dir="rtl">
  <input type="range" min="0" max="100" step="1" value="30" />
</div>

🛠️ Разработка

npm install
npm start    # dev-сервер с демо на src/view/index.html
npm run build # production-сборка в dist/

About

✨ A lightweight (~17kB minified) library to create input range sliders with one or two drag handles

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages