[Из песочницы] Создание слайдера диапазона значений для фильтра без использования Jquery

Очень часто на сайтах интернет-магазинов, и не только, можно фильтры со слайдером выбора диапазона значений. В одном из проектов мне тоже понадобилось сделать такой слайдер.

Первое, что приходит в голову — найти уже готовый и вставить себе на сайт. Тут то я и столкнулся с проблемой. Найденный мною плагин JqueryUI.slider отказался работать на некоторых мобильных устройствах. Проблему я выявить не смог (да не особо и хотел ковыряться в чужом коде) и решил сделать свой «велосипед».

«Велосипед» я решил делать на чистом JavaScript, чтобы не связываться с библиотеками. Как говорит один мой знакомый: «чем меньше «левых» подключений, тем стабильней все работает».

Поэтому, первым делом сверстал такой вот простенький макет

Фильтр 1
1050
Фильтр 2
05

На экране он выглядит так
lvwzjqatnfexfo9w5yj4l0vccmi.png
Сам слайдер с ползунками находится в блоке с классом «slider» в котором есть еще 2 блока: ползунок минимального значения и ползунок максимального значения диапазона.
В них описаны два события:


  1. onMouseDown — срабатывает, когда мы захватываем мышью ползунок;
  2. onTouchStart — срабатывает на мобильных устройствах, при касании пальца к нашему ползунку.

Над slider-ом размещается блок с двумя числами — это и есть наши максимальное и минимальное значения.

05

Теперь перейдем к самой функции, которая вызывается при клике.

function moveRange (elem) {
        //Определяем размеры и координаты нашего ползунка
    var coords = getCoords(elem);

    /*Определяем зону окрашивания*/
    var colorRange = elem.parentElement.children[1];
    var f;//устанавливаем флаг для определения мин или макс элемента
    var value; //значение фильтра

    /*Определяем второй ползунок и родителя*/
    var parent = {}
        parent.element = elem.parentElement;
        parent.coords = getCoords(parent.element);

    var block2 = {}
    if (elem.classList.contains('block-min')) {
        block2.element = elem.parentElement.children[2];
        block2.coords = getCoords(block2.element);
        f=0;
    } else {
        block2.element = elem.parentElement.children[0];
        block2.coords = getCoords(block2.element);
        f=1;
    }

    /*Делаем индикатор вывода значений над ползунком*/
    var indicator = document.createElement('div');
    if (elem.children.length){
        elem.innerHTML = '';//обнуляем предыдущее значение
    }
    elem.append(indicator);

    document.addEventListener('mousemove', onMouseMove);//определяем функцию-обработчик на событие движения мышью
    document.addEventListener('mouseup', onMouseUp);//определяем функцию-обработчик на событие отпускания кнопки мыши
    document.addEventListener('touchmove', onMouseMove);//здесь все тоже самое, только на касание пальцем
    document.addEventListener('touchend', onMouseUp);

    /*выключаем браузерное событие DaD*/
    elem.ondragstart = function(){
        return false;
    }

Функция getCoords позволяет нам получить координаты и размеры наших элементов.
Выглядит она следующим образом.

function getCoords(elem) {
    /*Получаем координаты относительно окна браузера*/
    let coords = elem.getBoundingClientRect();
    /*Высчитываем значения координат относительно документа, вычисляя прокрутку документа*/
    return {//возвращает объект, который содержит:
        top : coords.top + window.pageYOffset, //верхнюю координату элемента относительно страницы
        left : coords.left + window.pageXOffset, //крайнюю левую координату элемента относительно страницы
        leftX: coords.left, //левую координату относительно страницы
        rigth : coords.left + window.pageXOffset + coords.width, //крайнюю правую координату
        bottom : coords.top + window.pageYOffset + coords.height, //нижнюю координату
        width : coords.width //ширину элемента
    }
}

Теперь опишем функции-обработчики.
Первая функция — onMouseMove, которая вызывается при движении мышью. Она будет отрабатывать только при горизонтальном движении.

function onMouseMove(e) {

        /*Определяем смещение влево*/
        e.preventDefault();//предотвратить запуск выделения элементов

        /*Определяем положение мыши в зависимости от устройства*/
        /*На мобильных устройствах может фиксироваться несколько точек касания, поэтому используется массив targetTouches*/
        /*Мы будем брать только первое зафиксированое касание по экрану targetTouches[0]*/
        if (e.touches === undefined) {
            var pos = e.clientX;
        } else {
            var pos = e.targetTouches[0].clientX;
        }

        /*Устанавливаем границы движения ползунка*/
        let newLeft = pos - parent.coords.leftX;
        let rigthEdge = parent.coords.width - (coords.width+1);

        if (newLeft<0) {
            newLeft = 0;
        } else if (newLeft > rigthEdge) {
            newLeft = rigthEdge;
        }
        if (f == 0 && pos > block2.coords.left-block2.coords.width) {
            newLeft = block2.coords.left - block2.coords.width - 5 - parent.coords.leftX;
        }else if (f == 1 && pos < block2.coords.rigth + 5) {
            newLeft = block2.coords.rigth + 5 - parent.coords.leftX;
        }
        /*устанавливаем отступ нашему элементу*/
        elem.style.left = newLeft + 'px';

        //     Определяем значение фильтра
        let rangeMin = +document.querySelector('.filter number:first-child').innerHTML;
        let rangeMax = +document.querySelector('.filter number:last-child').innerHTML;
        if(f==0){
          value =  (newLeft / (parent.coords.width / (rangeMax - rangeMin)) + rangeMin).toFixed(1);
        } else {
          value = (newLeft / (parent.coords.width / (rangeMax - rangeMin))+ 0.3 + rangeMin).toFixed(1);
        }

        /*Выводим значение над ползунком*/
        indicator.style.position = 'absolute';
        indicator.style.fontSize = "14px";
        indicator.style.left = - coords.width/2 + "px";
        indicator.style.top = parseFloat(window.getComputedStyle(elem).getPropertyValue('top')) - 10 +"px";

        /*Для красоты слайдера уберем вывод значений в начальной и конечной точках*/
        if (newLeft <= 0){
            indicator.innerHTML= "";
        } else if (newLeft >= rigthEdge) {
            indicator.innerHTML= "";
        } else {
            indicator.innerHTML = value;
        }

        /*Делаем цветную плашечку диапазона выбора*/
        if (f == 0) {
            colorRange.style.left = newLeft + coords.width + "px";
            colorRange.style.width = block2.coords.left - getCoords(elem).left - coords.width + "px";
        }  else {
            colorRange.style.left = block2.coords.left - parent.coords.leftX + "px";
            colorRange.style.width = getCoords(elem).left - block2.coords.left + "px";
        }    
}

И, наконец, функция-обработчик события «отпускания кнопки» или потери точки касания на мобильных устройствах. Она удаляет все добавленные ранее события и оставляет ползунок на установленном значении.

function onMouseUp() {
        document.removeEventListener('mouseup', onMouseUp);
        document.removeEventListener('mousemove', onMouseMove);
        document.removeEventListener('touchend', onMouseUp);
        document.removeEventListener('touchmove', onMouseMove);
    }

И, конечно же, CSS-стили для нашего слайдера

.filter {
    padding: 30px;
    width: 500px;
}
.filter>div {
    padding-top: 20px;
    display: -webkit-flex;
    display: -moz-flex;
    display: -ms-flex;
    display: -o-flex;
    display: flex;
    -webkit-flex-direction: column;
    -moz-flex-direction: column;
    -ms-flex-direction: column;
    -o-flex-direction: column;
    flex-direction: column;
}
.filter>div>div {
    display: -webkit-flex;
    display: -moz-flex;
    display: -ms-flex;
    display: -o-flex;
    display: flex;
    justify-content: space-between;
}
.filter .slider {
    margin-top: 10px;
    position: relative;
    height: 6px;
    background: #fff;
    border: 1px solid #000;
}
.filter .color-range {
    position: absolute;
    background: #a4a4a4;
    width: 97%;
    border: none;
    height: 6px;
    left: 15px;
}
.filter .block-min, .block-max {
  width: 15px;
  height: 25px;
  position: absolute;
  left: 0;
  top: -11.5px;
  background: #fff;
  border: 1px solid #000;
  border-radius: 4px;
  z-index: 1;
}
.filter .block-max{
    left: 97%;
}

Посмотреть рабочую версию данного слайдера можно здесь.

Все файлы можно скачать с GitHub.

Приятного пользования и легкой работы!

© Habrahabr.ru