1

I am very new to this and i am trying to figure out how to solve my issue. I am trying to design an Range slider for controling the speed of an machine application. I have been searching here for many hours now but i can not find the right result for my issue. The code i am adding here is something i have found other pages so it might not be best solution.

I would like to do 3 different things with it to start with.

  1. First issue is that i would like the handle to snap to the ticks i have made, is it possible to do that with code so if i change my Ticks the handle will stil snap to the new ticks?
  2. Is it possible to have more ticks between 0 and 10% so i have like 2% steps there up to 10% and when the handle is there it will then snap to the 2% steps and when its above 10% it will snap to the other 10% step interval.
  3. Is it possible to move the Ticks inside the slider instead and have the labels in the middle? I would like to have the ticks both in the bottom and in the top and then the labels in the middle. I have attached a picture of what i am aiming for.

Many thanks

Range slider idea!

Link to my Code

https://www.w3schools.com/code/tryit.asp?filename=GT1V8EFG2RUA

OSMO
  • 31
  • 2

2 Answers2

0

function toggleStep(element) {
  if (element.value >= 10) { element.step = 10; }
  else { element.step = 2; }
}

/**
 * Sniffs for Older Edge or IE,
 * more info here:
 * https://stackoverflow.com/q/31721250/3528132
 */
function isOlderEdgeOrIE() {
  return (
    window.navigator.userAgent.indexOf("MSIE ") > -1 ||
    !!navigator.userAgent.match(/Trident.*rv\:11\./) ||
    window.navigator.userAgent.indexOf("Edge") > -1
  );
}

function valueTotalRatio(value, min, max) {
  return ((value - min) / (max - min)).toFixed(2);
}

function getLinearGradientCSS(ratio, leftColor, rightColor) {
  return [
    '-webkit-gradient(',
    'linear, ',
    'left top, ',
    'right top, ',
    'color-stop(' + ratio + ', ' + leftColor + '), ',
    'color-stop(' + ratio + ', ' + rightColor + ')',
    ')'
  ].join('');
}

function updateRangeEl(rangeEl) {
  var ratio = valueTotalRatio(rangeEl.value, rangeEl.min, rangeEl.max);

  rangeEl.style.backgroundImage = getLinearGradientCSS(ratio, '#00FF00', '#c5c5c5');
}

function initRangeEl() {
  var rangeEl = document.querySelector('input[type=range]');
  var textEl = document.querySelector('input[type=text]');

  /**
   * IE/Older Edge FIX
   * On IE/Older Edge the height of the <input type="range" />
   * is the whole element as oposed to Chrome/Moz
   * where the height is applied to the track.
   *
   */
  if (isOlderEdgeOrIE()) {
    rangeEl.style.height = "20px";
    // IE 11/10 fires change instead of input
    // https://stackoverflow.com/a/50887531/3528132
    rangeEl.addEventListener("change", function(e) {
      textEl.value = e.target.value;
    });
    rangeEl.addEventListener("input", function(e) {
      textEl.value = e.target.value;
    });
  } else {
    updateRangeEl(rangeEl);
    rangeEl.addEventListener("input", function(e) {
      updateRangeEl(e.target);
      textEl.value = e.target.value;
    });
  }
}

initRangeEl();
.slidecontainer {
  width: 500px;
  margin-left:15px;
}

input[type="range"] {
  -webkit-appearance: none;
  -moz-appearance: none;
  width:100%;
  height: 50px;
  padding: 0;
  border: solid 2px #000000;
  border-radius: 8px;
  outline: none;
  cursor: pointer;
}

datalist {
  display: flex;
  margin-left: 2px;
  margin-top: -50px;
}

datalist option {
  flex-basis: 10%;
  border-left: 1px solid #000;
}
datalist option:nth-child(-n+5) {
  flex-basis: 2%;
}
datalist option:last-child {
  display: none;
}

/*Chrome thumb*/

input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  -moz-appearance: none;
  -webkit-border-radius: 5px;
  /*16x16px adjusted to be same as 14x14px on moz*/
  height: 45px;
  width: 45px;
  border-radius: 5px;
  background: #e7e7e7;
  border: 2px solid #000000;
  opacity: 0.4;
  cursor: pointer;
  border-radius: 8px; 

}


/*Mozilla thumb*/

input[type="range"]::-moz-range-thumb {
  -webkit-appearance: none;
  -moz-appearance: none;
  -moz-border-radius: 5px;
  height: 14px;
  width: 14px;
  border-radius: 5px;
  background: #e7e7e7;
  border: 1px solid #c5c5c5;
}


/*IE & Edge input*/

input[type=range]::-ms-track {
  width: 300px;
  height: 6px;
  /*remove bg colour from the track, we'll use ms-fill-lower and ms-fill-upper instead */
  background: transparent;
  /*leave room for the larger thumb to overflow with a transparent border */
  border-color: transparent;
  border-width: 2px 0;
  /*remove default tick marks*/
  color: transparent;
    margin-left:15px;
}


/*IE & Edge thumb*/

input[type=range]::-ms-thumb {
  height: 14px;
  width: 14px;
  border-radius: 5px;
  background: #e7e7e7;
  border: 1px solid #c5c5c5;
}


/*IE & Edge left side*/

input[type=range]::-ms-fill-lower {
  background: #000000;
  border-radius: 2px;
    margin-left:15px;
}


/*IE & Edge right side*/

input[type=range]::-ms-fill-upper {
  background: #000000;
  border-radius: 2px;
}


/*IE disable tooltip*/

input[type=range]::-ms-tooltip {
  display: none;
}

input[type="text"] {
  border: none;
}


.sliderticks {
  display: flex;
  justify-content: space-between;
  padding: 0 50px;
}

.sliderticks p {
  position: relative;
  display: flex;
  justify-content: center;
  text-align: center;
  width: 1px;
  background: #D3D3D3;
  height: 10px;
  line-height: 40px;
  margin: 0 0 20px 0;
}
<h1>Custom Range Slider</h1>
<p>Drag the slider to display the current value.</p>

<div class="slidecontainer">
<input type="range" value="10" min="0" max="100" oninput="toggleStep(this)" list="sliderticks"   />

<div class="datalist">
 <datalist id="sliderticks">
   <option>0</option>
   <option>2</option>
   <option>4</option>
   <option>6</option>
   <option>8</option>
   <option>10</option>
   <option>20</option>
   <option>30</option>
   <option>40</option>
   <option>50</option>
   <option>60</option>
   <option>70</option>
   <option>80</option>
   <option>90</option>
   <option>100</option>
 </datalist>
</div>

</div>
idfurw
  • 5,727
  • 2
  • 5
  • 18
0

Hope the code below solves your needs:

document.querySelectorAll('.range input[type="range"]').forEach((range) => {
  range.tickmarks = [...range.list.options].map((el) => parseFloat(el.value));
  range.list.insertAdjacentHTML('afterend', `<div class="range-value"></div>`);
  range.max = parseFloat(range.tickmarks[range.tickmarks.length - 1]);
  let q = (range.clientWidth - 40) / range.max;
  range.addEventListener('input', function(ev) {
    if (this.hasAttribute('ticks-only')) {
      this.value = range.tickmarks.map((x) => x).sort((x, y) => Math.abs(x - this.value) - Math.abs(y - this.value))[0];
    }
    this.parentElement.style.backgroundSize = `${(this.value * range.clientWidth) / range.max}px 100%, 100% 100%`;
    this.list.nextElementSibling.textContent = this.value;
    this.list.nextElementSibling.style.left = `${20 + this.value * q}px`;
  });
  range.tickmarks.forEach((el, i) => {
    range.list.options[i].label = el % 10 ? ' ' : el;
    range.list.options[i].style.left = 20 + el * q + 'px';
  });
  range.dispatchEvent(new Event('input', {bubbles: true}));
});
.range {
  position: relative;
  height: 52px; width: 500px;
  border-radius: 0.125em;
  overflow: hidden;
  font: 16px/1em sans-serif;
  background-image: linear-gradient(to right, #7bad23, #39594c), linear-gradient(to bottom, #84929d, #46576d);
  background-size: 0 100%, 100% 100%;
  background-repeat: no-repeat;
  box-shadow: 0 0 0 0.125em #7f8c97, 0.0625em 0.125em 0.3125em 0em #93a2bd, -0.125em -0.1875em 0.3125em 0em #0e1116, inset 0 0 1.25em -0.3125em #0e1116;
}

datalist {
  position: relative;
  display: block;
  height: 100%; width: 100%;
  pointer-events: none;
}

.range datalist option {
  position: absolute !important;
  display: grid;
  place-items: center;
  height: 100%;
  padding: 0;
  transform: translatex(-50%);
  font: bold 12px/1em monospace !important;
  text-shadow: 1px 0 0.16666667em #fff5;
}

.range datalist option::before,
.range datalist option::after {
  content: "";
  position: absolute;
  left: 50%;
  height: 100%; width: 0;
  box-shadow: 0 0 0 0.08333333em #0008;
}
.range datalist option::before { bottom: calc(50% + 1em); }
.range datalist option::after { top: calc(50% + 1em); }

input[type="range"] {
  appearance: none;
  position: absolute;
  top: 0; left: 0; z-index: 1;
  margin: 0;
  height: 100%; width: 100%;
  box-sizing: border-box;
  font: inherit;
  background-color: transparent;
  cursor: pointer;
}

input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  display: block;
  width: 2.5em; height: 2.5em;
  border: none;
  border-radius: 0.75em;
  background-color: #99c7;
  box-shadow: 0 0 0.125em 0 #282850dd, inset 0 0 0.125em 0.125em #282850dd;
  transition: background-color 0.5s ease-out 0.5s;
}
input[type="range"]:active::-webkit-slider-thumb {
  background-color: #fff8;
  transition: background-color 0.5s ease-out;
}

.range-value {
  position: absolute;
  top: 0; left: 0; z-index: 2;
  display: grid;
  place-items: center;
  height: 100%;
  transform: translatex(-50%);
  font: bold 12px/1em monospace !important;
  text-shadow: 0 0 0.16666667em #fff;
  opacity: 0;
  pointer-events: none;
  transition: opacity 1s ease-in;
}
input[type="range"]:focus-visible~.range-value,
input[type="range"]:active~.range-value,
input[type="range"]:hover~.range-value {
  opacity: 1;
  transition: opacity 0.5s ease-out;
}

body { margin: 0; height: 100vh; background-color: #42516c; display: flex; flex-flow: column nowrap; justify-content: center; align-items: center; }
<div class="range">
  <input type="range" list="high">
  <datalist id="high">
    <option value="0">
    <option value="2">
    <option value="4">
    <option value="6">
    <option value="8">
    <option value="10">
    <option value="20">
    <option value="30">
    <option value="40">
    <option value="50">
    <option value="60">
    <option value="70">
    <option value="100">
  </datalist>
</div>
<h3>With "ticks-only" attribute</h3>
<div class="range">
  <input type="range" list="bass" ticks-only>
  <datalist id="bass">
    <option value="0">
    <option value="10">
    <option value="15">
    <option value="20">
    <option value="30">
    <option value="40">
    <option value="50">
    <option value="60">
    <option value="70">
    <option value="72">
    <option value="74">
    <option value="76">
    <option value="78">
    <option value="80">
    <option value="100">
    <option value="175">
    <option value="200">
  </datalist>
</div>

Note! This code only works for "webkit" (tested in Chrome). Firefox does not work correctly with the "position" property when parent and child elements are set to "absolute".

UModeL
  • 1,217
  • 1
  • 10
  • 15