Customized Range Slider


The native HTML5 control range input has different styles for each browser. In order to achieve a unified style effect, we usually have two solutions to consider, directly modify the CSS style, or simulate a slider control.

Solution One

Modify the css style directly. Use -webkit-slider-thumb to modify the style of the slider, use -webkit-slider-runnable-track to modify the style of the track, and then combine events such as :focus and :hover to add special effects to make the slider More vivid.

The following describes how to control the style of the input range only through css.

Make a simple demonstration

Or click our Online Demo


We have demonstrated the slider style under webkit here. If you want to support more browsers, you need to set different pseudo-classes.

  • ::-webkit-slider-thumb set slider for webkit/blink

  • ::-moz-range-thumb set the slider for Firefox

  • ::-ms-thumb Set slider for IE

There is also the setting of the background bar

  • ::-webkit-slider-runnable-track set background bar for webkit/blink

  • ::-moz-range-track set the background bar for Firefox

  • ::-ms-track/::-ms-fill-lower/::-ms-fill-upper set background bar for IE


We placed a vertical line in the middle of the slider to indicate the middle position. In addition to using the key shift+\ to type out the vertical line symbol, there are some of the following vertical line symbols that can be used.


Solution Two

Simulates a slider control. For example, use the div element to simulate the range input style, and use pointdown, pointmove, pointup and other events to simulate the behavior of input

code show as below

<div id="container">
  <div id="slider" class="slider">
    <div class="thumb"></div>

  <div class="rate">50%</div>

Organize the style

#container {
  margin: 30% auto;
  width: 300px;
  text-align: center;
.slider {
  border-radius: 6px;
  background: #e0e0e0;
  height: 10px;

.thumb {
  touch-action: none;
  width: 20px;
  height: 20px;
  border-radius: 20px;
  position: relative;
  left: 140px;
  top: -5px;
  background: gray;
  cursor: pointer;
  transition: transform 0.3s;
.rate {
  margin-top: 20px;

write event

let thumb = slider.querySelector(".thumb");
let rate = container.querySelector(".rate");
let shiftX;
const total = slider.offsetWidth - thumb.offsetWidth;

function onThumbDown(event) {
  event.preventDefault(); // prevent selection start (browser action)

  shiftX = event.clientX - thumb.getBoundingClientRect().left;

  thumb.setPointerCapture(event.pointerId); = "scale(1)";

  thumb.onpointermove = onThumbMove;

  thumb.onpointerup = (event) => {
    // dragging finished, no need to track pointer any more
    // ...any other "drag end" logic here...
    thumb.onpointermove = null;
    thumb.onpointerup = null; = "scale(1.2)";

function onThumbMove(event) {
  let newLeft = event.clientX - shiftX - slider.getBoundingClientRect().left;

  // if the pointer is out of slider => adjust left to be within the boundaries
  if (newLeft < 0) {
    newLeft = 0;
  let rightEdge = slider.offsetWidth - thumb.offsetWidth;
  if (newLeft > rightEdge) {
    newLeft = rightEdge;
  } = newLeft + "px";

function onThumbEnter(event) { = "scale(1.2)";

function onThumbLeave(event) { = "scale(1)";

function setRate(left) {
  rate.innerText = ((left / total) * 100).toFixed(0) + "%";
thumb.onpointerdown = onThumbDown;
thumb.onpointerenter = onThumbEnter;
thumb.onpointerleave = onThumbLeave;

thumb.ondragstart = () => false;

see the actual effect


For more detailed interpretation, please refer to the following blog post