How to fix a custom range slider progress not following input range thumb?

1.9k Views Asked by At

I am trying to make a custom range slider with a progress bar using jQuery in HTML/CSS for Google Chrome browser. I want to make an input type range with a progress bar that follows the input thumb not like my custom input range.

My code is below:

var range2 = $("#newSlider");
var progressBar2 = $(".progressslider");
$(range2).on("input change", function() {
  progressBar2.css("width", $(this).val() + "%");
  rangeValue.text($(this).val() + "%");
});
.boxslider {
  width: 400px;
  position: relative;
}

#newSlider {
  -webkit-appearance: none;
  width: 400px;
  height: 20px;
  background-color: black;
  outline: none;
  cursor: pointer;
}

#newSlider::-webkit-slider-thumb {
  width: 30px;
  height: 30px;
  background-color: rgb(243, 12, 12);
  border-radius: 50%;
  -webkit-appearance: none;
  position: relative;
  z-index: 5;
  cursor: pointer;
}

.progressslider {
  width: 10px;
  height: 20px;
  background-color: rgb(224, 84, 91);
  position: absolute;
  top: 2px;
  left: 0;
  pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="boxslider">
  <input type="range" id="newSlider" min="0" max="100" value="5">
  <div class="progressslider"></div>
</div>

2

There are 2 best solutions below

0
On

change rangeValue to $(this)

var range2 = $("#newSlider");
var progressBar2 = $(".progressslider");
$(range2).on("input change", function() {
  progressBar2.css("width", $(this).val() + "%");
 console.log(this)
  $(this).text($(this).val() + "%");
});
.boxslider {
  width: 400px;
  position: relative;
}

#newSlider {
  -webkit-appearance: none;
  width: 400px;
  height: 20px;
  background-color: black;
  outline: none;
  cursor: pointer;
}

#newSlider::-webkit-slider-thumb {
  width: 30px;
  height: 30px;
  background-color: rgb(243, 12, 12);
  border-radius: 50%;
  -webkit-appearance: none;
  position: relative;
  z-index: 5;
  cursor: pointer;
}

.progressslider {
  width: 10px;
  height: 20px;
  background-color: rgb(224, 84, 91);
  position: absolute;
  top: 2px;
  left: 0;
  pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="boxslider">
  <input type="range" id="newSlider" min="0" max="100" value="5">
  <div class="progressslider"></div>
</div>

0
On

You want to account for the handle width, i.e. CSS would look something like this:

width: calc(15px + ${$(this).val()} * (100% - 30px));

where 30px is the handle width, 15px is half of that, and ${$(this).val()} is the current value of the slider between 0 and 1.

In fact you don't need a separate div, but can use gradients to fill different parts of the track.

You can use input range slider CSS styler to dial that in (and more) to style input range slider across browsers. I made handle color transparent so you can see how progress is drawn:

for (let e of document.querySelectorAll('input[type="range"].slider-progress')) {
  e.style.setProperty('--value', e.value);
  e.style.setProperty('--min', e.min == '' ? '0' : e.min);
  e.style.setProperty('--max', e.max == '' ? '100' : e.max);
  e.addEventListener('input', () => e.style.setProperty('--value', e.value));
}
/*generated with Input range slider CSS style generator (version 20211219)
https://toughengineer.github.io/demo/slider-styler*/
input[type=range].styled-slider {
  height: 32px;
  -webkit-appearance: none;
}

/*progress support*/
input[type=range].styled-slider.slider-progress {
  --range: calc(var(--max) - var(--min));
  --ratio: calc((var(--value) - var(--min)) / var(--range));
  --sx: calc(0.5 * 30px + var(--ratio) * (100% - 30px));
}

input[type=range].styled-slider:focus {
  outline: none;
}

/*webkit*/
input[type=range].styled-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: #F30C0C80;
  border: none;
  box-shadow: none;
  margin-top: calc(1em * 0.5 - 30px * 0.5);
}

input[type=range].styled-slider::-webkit-slider-runnable-track {
  height: 1em;
  border: none;
  border-radius: 0;
  background: #000000;
  box-shadow: none;
}
input[type=range].styled-slider.slider-progress::-webkit-slider-runnable-track {
  background: linear-gradient(#E0545B,#E0545B) 0/var(--sx) 100% no-repeat, #000000;
}

/*mozilla*/
input[type=range].styled-slider::-moz-range-thumb {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: #F30C0C80;
  border: none;
  box-shadow: none;
}

input[type=range].styled-slider::-moz-range-track {
  height: 1em;
  border: none;
  border-radius: 0;
  background: #000000;
  box-shadow: none;
}

input[type=range].styled-slider.slider-progress::-moz-range-track {
  background: linear-gradient(#E0545B,#E0545B) 0/var(--sx) 100% no-repeat, #000000;
}

/*ms*/
input[type=range].styled-slider::-ms-fill-upper {
  background: transparent;
  border-color: transparent;
}

input[type=range].styled-slider::-ms-fill-lower {
  background: transparent;
  border-color: transparent;
}

input[type=range].styled-slider::-ms-thumb {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: #F30C0C80;
  border: none;
  box-shadow: none;
  margin-top: 0;
  box-sizing: border-box;
}

input[type=range].styled-slider::-ms-track {
  height: 1em;
  border-radius: 0;
  background: #000000;
  border: none;
  box-shadow: none;
  box-sizing: border-box;
}

input[type=range].styled-slider.slider-progress::-ms-fill-lower {
  height: 1em;
  border-radius: 0px 0 0 0px;
  margin: -undefined 0 -undefined -undefined;
  background: #E0545B;
  border: none;
  border-right-width: 0;
}
<input type="range" class="styled-slider slider-progress" style="width:400px;"/>