Можно ли сделать это без js или хотя бы не имитируя работу ползунка при помощи js?
Совсем без JS не получится. Но, без имитации и сложных скриптов, вполне возможно.
Для этого назначаем кнопке ползунка множественный фон и задаём необходимый градиент. Также, для удобства изменения значений в нескольких местах сразу, используем CSS-переменные (к тому же они используются в некоторых вычислениях).
Главный (и наверное единственный) минус - невозможность указать ширину в процентах.
const GRADIENT_RANGE = document.querySelector('.gradient');
GRADIENT_RANGE.addEventListener('input', function(ev) {
let percent = (100 * (+this.value - +this.min)) / (+this.max - +this.min);
this.style.setProperty('--percent', percent);
});
GRADIENT_RANGE.dispatchEvent(new Event('input'));
input[type="range"] {
--width: 90vw;
/* --gradient: linear-gradient(90deg, #101b69 50%, #a90043 50%); оригинальный */
--gradient: linear-gradient(90deg, red, orange, yellow, lime, cyan, blue, purple);
--thumb-sz: 133px;
--thumb-br: 10px;
--thumb-bg:
50% 50% / 100% 100% radial-gradient(circle calc(var(--thumb-sz) / 2 - var(--thumb-br)), #fff 0 98%, #fff0 100%) no-repeat,
calc(var(--percent) * 1%) 0 / var(--width) 100% var(--gradient) no-repeat;
--track-h: 24px;
-webkit-appearance: none;
margin: calc(var(--thumb-sz) / 2) auto;
display: block;
width: var(--width);
}
input[type="range"]:focus { outline: none; }
input[type="range"]::-webkit-slider-runnable-track {
height: var(--track-h);
width: 100%;
border-radius: 1000px;
background: var(--gradient);
cursor: pointer;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
position: relative;
top: 50%;
height: var(--thumb-sz);
width: var(--thumb-sz);
border-radius: 50%;
transform: translateY(-50%);
background: var(--thumb-bg);
cursor: pointer;
}
input[type="range"]::-moz-range-track {
height: var(--track-h);
width: 100%;
border: none;
border-radius: 1000px;
background: var(--gradient);
cursor: pointer;
}
input[type="range"]::-moz-range-thumb {
-webkit-appearance: none;
height: var(--thumb-sz);
width: var(--thumb-sz);
border: none;
border-radius: 50%;
background: var(--thumb-bg);
cursor: pointer;
}
input[type="range"]::-ms-track {
height: var(--track-h);
width: 100%;
cursor: pointer;
border-color: transparent;
color: transparent;
background: transparent;
}
input[type="range"]::-ms-fill-lower {
border-radius: 1000px;
background: var(--gradient);
}
input[type="range"]::-ms-fill-upper {
border-radius: 1000px;
background: var(--gradient);
}
input[type="range"]::-ms-thumb {
-webkit-appearance: none;
position: relative;
top: 50%;
height: var(--thumb-sz);
width: var(--thumb-sz);
border: none;
border-radius: 50%;
transform: translateY(-50%);
background: var(--thumb-bg);
cursor: pointer;
}
<input class="gradient" type="range" min="500" max="800000" value="250000" name="range" step="1">
Извините, но префиксы -ms-
негде было протестировать :-)
в моем случае градиент у полоски и у ползунка должен быть различным. В добавок, градиент у ползунка не должен меняться при перемещении.
Тогда задача упрощается - убираем JS и добавляем треку и кнопке разные градиенты. В таком варианте, работает даже процентная ширина ползунка.
input[type="range"] {
--track-gradient: linear-gradient(90deg, #101b69 0%, #a90043 100%);
--thumb-gradient: conic-gradient(red, orange, yellow, lime, cyan, blue, purple, red);
--thumb-sz: 133px;
--thumb-br: 10px;
--thumb-bg:
50% 50% / 100% 100% radial-gradient(circle calc(var(--thumb-sz) / 2 - var(--thumb-br)), #fff 0 98%, #fff0 100%) no-repeat,
50% 50% / 100% 100% var(--thumb-gradient) no-repeat;
--track-h: 24px;
-webkit-appearance: none;
margin: calc(var(--thumb-sz) / 2) auto;
display: block;
width: 100%;
}
input[type="range"]:focus { outline: none; }
input[type="range"]::-webkit-slider-runnable-track {
height: var(--track-h);
width: 100%;
border-radius: 1000px;
background: var(--track-gradient);
cursor: pointer;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
position: relative;
top: 50%;
height: var(--thumb-sz);
width: var(--thumb-sz);
border-radius: 50%;
transform: translateY(-50%);
background: var(--thumb-bg);
cursor: pointer;
}
input[type="range"]::-moz-range-track {
height: var(--track-h);
width: 100%;
border: none;
border-radius: 1000px;
background: var(--track-gradient);
cursor: pointer;
}
input[type="range"]::-moz-range-thumb {
-webkit-appearance: none;
height: var(--thumb-sz);
width: var(--thumb-sz);
border: none;
border-radius: 50%;
background: var(--thumb-bg);
cursor: pointer;
}
input[type="range"]::-ms-track {
height: var(--track-h);
width: 100%;
cursor: pointer;
border-color: transparent;
color: transparent;
background: transparent;
}
input[type="range"]::-ms-fill-lower {
border-radius: 1000px;
background: var(--track-gradient);
}
input[type="range"]::-ms-fill-upper {
border-radius: 1000px;
background: var(--track-gradient);
}
input[type="range"]::-ms-thumb {
-webkit-appearance: none;
position: relative;
top: 50%;
height: var(--thumb-sz);
width: var(--thumb-sz);
border: none;
border-radius: 50%;
transform: translateY(-50%);
background: var(--thumb-bg);
cursor: pointer;
}
<input class="gradient" type="range" min="500" max="800000" value="250000" name="range" step="1">