2

I am working with a HTML5 input range, and I need to style the slider to have a different colour (black) and width(2px) up to the position of the slider and to have it set to have #393837 and 1px after the slider.
For illustration the image below, where it's darker and thicker before and thinner after:

slider

What I've achieved so far is this, to have #393837 colour throughout the slider.

slider with lighter color

.slider {
  width: 100%;
  height: 1px;
  outline: none;
  background: #393837;
  -webkit-appearance: none;
}

.slider::-webkit-slider-thumb {
  width: 8px;
  height: 8px;
  cursor: pointer;
  appearance: none;
  background: black;
  border-radius: 50%;
  -webkit-appearance: none;
}

.slider::-moz-range-thumb {
  width: 8px;
  height: 8px;
  border: none;
  cursor: pointer;
  border-radius: 50%;
  background: black;
}
<input min="1" max="100" type="range" className="slider" />

P.S. I'm working with reactJS, so I don't wish to use jQuery

NOTE: My question has been marked as a duplicate, but the solutions in the other question are using a hardcoded width for the input element, where I don't.

Tschallacka
  • 27,901
  • 14
  • 88
  • 133
Vinay Sharma
  • 3,291
  • 4
  • 30
  • 66

1 Answers1

0

By using a "illusion" element to pretend to be part of the slider you can achieve an effect like you wish to do.

I'm not all to familiar with ReactJS as of yet, so I coded it in vanilla javascript, but it should be easy enough to convert to react code.

By listening to the change and input events on the slider, you can update the "illusion" to become smaller or larger.
By listening to clicks on the illusion, you can make the slider have a larger value and then trigger the change event to update the width of the illusion.

Do note that I used scoped references in my code. Depending on your code base you might wish to make these class properties or fetch them anew every time an event is triggered.

Hopefully this gives you inspiration enough to come up to a react solution.

/**
 * getting all the sliders in the document.
 * define how big the thumb is here.
 */
const SLIDER_THUMB_SIZE = 8;
Array.from(document.querySelectorAll('.slider'))
      .forEach(function(element) {
         /** a reference to our illusion of a slider **/
         let illusion = element.parentNode.querySelector('.slider-illusion');
         /** We need this to save our state without having to look it up on the element **/
         let currentWidth;
         
         /** Our change handler, to update the width of our thin slider piece **/
         let changeWidth = (e) => {
             
             /** get the current actual width of our input element **/
             let width = parseInt(window.getComputedStyle(element).getPropertyValue("width"));
             /** Calculate the left offset **/
             let left = ((element.value - element.min) / (element.max - element.min)) * ((width - SLIDER_THUMB_SIZE)) + SLIDER_THUMB_SIZE * 1.25 ;
             /** set the proper width **/
             illusion.style.width = (width - left + SLIDER_THUMB_SIZE) + "px";
             /** update out position **/
             illusion.style.left = left+"px" ;  
         };
         
         /** When a click is registered on the illusion, we need to update our
             value in the slider. **/
         let illusionTrigger = (e) => {
             /** Where was clicked in the illusion? **/
             let offset = e.offsetX;
             /** how wide are the parts in our illusion for each change **/
             let part = illusion.clientWidth / (element.max - element.value) ;
             /** calculate the value that needs to be added to the current slider value **/
             let change = Math.round(offset / part);
             /** force a change if there is a click behind the thumb **/
             if(change == 0) {
                 change = 1;
             }
             /** update the slider **/
             element.value = parseInt(element.value) + change;
             /** Trigger a change notification and a redraw of the illusion **/
             element.dispatchEvent(new Event('change'));
         };
         /** trigger it once to update everything to the correct position **/
         changeWidth();
         /** bind the event handler to the input and change event to enable dragging of the thumb**/
         element.addEventListener('input', changeWidth);
         element.addEventListener('change', changeWidth);
         let forFun = (e) => {
              document.getElementById(element.name).innerText = element.value;
         };
         element.addEventListener('input', forFun);
         element.addEventListener('change', forFun);
         /** listen to the click event on the illusion **/
         illusion.addEventListener('click', illusionTrigger);
});
.slider-container {
    position:relative;
    height: 10px;
    width: 100%;
    overflow: hidden;
}
.slider-illusion {
   position: absolute;
   top:0px;
   background: white;;
   width: 50%;
   left: calc(50% + 2px);
   height:10px;
   cursor: pointer;
}
.slider-illusion .line {
  position: absolute;
  top: 3px;
  height: 2px;
  background-color: black;
  width: 100%;

}
.slider {
  /**
   * absolute positioning to assure both elements align the same
   */
  position:absolute;
  top:0px;
  left:0px;
  
  width: 100%;
  height: 5px;
  outline: none;
  background: #393837;
  -webkit-appearance: none;
}

.slider::-webkit-slider-thumb {
  width: 8px;
  height: 8px;
  cursor: pointer;
  appearance: none;
  background: black;
  border-radius: 50%;
  -webkit-appearance: none;
}

.slider::-moz-range-thumb {
  width: 8px;
  height: 8px;
  border: none;
  cursor: pointer;
  border-radius: 50%;
  background: black;
}
Select how many cups you want: <span id="cups">3</span>/5
<div class="slider-container">
  <input min="1" max="5" value="3" type="range" class="slider" name="cups" />
  <div class="slider-illusion" data-width="50%">
    <div class="line"></div>
  </div>
</div>
<br>
Select how many balls you want: <span id="balls">125</span>/500
<div class="slider-container">
  <input min="1" max="500" value="125" type="range"  class="slider" name="balls" />
  <div class="slider-illusion" data-width="50%">
    <div class="line"></div>
  </div>
</div>
<br>
select how many rounds to play: <span id="rounds">3</span>/10
<div class="slider-container">
  <input min="1" max="10" value="3" type="range" class="slider" name="rounds"/>
  <div class="slider-illusion" data-width="50%">
    <div class="line"></div>
  </div>
</div>
Tschallacka
  • 27,901
  • 14
  • 88
  • 133
  • 1
    Thanks for sharing your solution, but the slider thumbs are cutting from the right side as the I start sliding to left :) – Vinay Sharma Jul 15 '20 at 09:16
  • @VinaySharma I updated my code, I was in the middle of revamping it as it failed on any other values than 1 - 100. How does it work now? – Tschallacka Jul 15 '20 at 09:33
  • 1
    It works perfect now! Just slightly visible edges on right side of thumb, I'll try to figure that out :) – Vinay Sharma Jul 15 '20 at 11:20
  • @VinaySharma Yea, that has to do with the overflow, etc... and can also change per other css rules present in your website. I just give the functionality code in how you can make it work. Also I fixed just now after your reply that when clicking next tot the thumb it would jump to the end. – Tschallacka Jul 15 '20 at 11:25
  • 1
    alright, thanks a lot for your solution. I'll go through it thoroughly. Will post my solution as well soon! – Vinay Sharma Jul 15 '20 at 11:56