Custom Range Slider With Sliding Value

Category: Form , Javascript | February 22, 2023
Author:Jon Kantner
Views Total:1,047 views
Official Page:Go to website
Last Update:February 22, 2023
License:MIT

Preview:

Custom Range Slider With Sliding Value

Description:

A sleek custom range slider control where users can easily select a value and see it displayed in real-time as they move the slider handle along the track.

How to use it:

1. Code the HTML for the range slider.

<form class="range" action="">
  <label class="range__label" for="example">Range Slider</label>
  <input class="range__input" id="example" type="range" value="25" min="0" max="100" step="1">
  <div class="range__output" aria-hidden="true" data-tip>
    <div class="range__output-value-track">
      <div class="range__output-values" data-values></div>
    </div>
  </div>
</form>

2. Apply CSS styles to the range slider.

/* CSS variables */
:root {
  --hue: 223;
  --white: hsl(0,0%,100%);
  --lt-gray: hsl(var(--hue),10%,95%);
  --primary0: hsl(var(--hue),90%,95%);
  --primary1: hsl(var(--hue),90%,90%);
  --primary3: hsl(var(--hue),90%,50%);
  --primary4: hsl(var(--hue),90%,30%);
  --primary5: hsl(var(--hue),90%,10%);
  --trans-dur: 0.3s;
  font-size: calc(16px + (32 - 16) * (100vw - 320px) / (1280 - 320));
}
/* Main styles */
.range {
  margin: 2.5em 0.75em 0 0.75em;
  padding-top: 0.5em;
  position: relative;
  max-width: 12em;
  width: 100%;
}
.range__label {
  overflow: hidden;
  position: absolute;
  width: 1px;
  height: 1px;
}
.range__input {
  --percent: 50%;
  background-color: var(--primary1);
  background-image: linear-gradient(var(--primary3),var(--primary3));
  background-size: var(--percent) 100%;
  background-repeat: no-repeat;
  border-radius: 0.25em;
  display: block;
  margin: 0.5em -0.75em;
  width: calc(100% + 1.5em);
  height: 0.5em;
  transition: background-color var(--trans-dur);
  -webkit-appearance: none;
  appearance: none;
  -webkit-tap-highlight-color: transparent;
}
.range__input:focus {
  outline: transparent;
}
/* WebKit */
.range__input::-webkit-slider-thumb {
  background-color: var(--white);
  border: 0;
  border-radius: 50%;
  box-shadow: 0 0.125em 0.5em hsl(0,0%,0%,0.3);
  width: 1.5em;
  height: 1.5em;
  transition: background-color 0.15s linear;
  -webkit-appearance: none;
  appearance: none;
}
.range__input:focus::-webkit-slider-thumb,
.range__input::-webkit-slider-thumb:hover {
  background-color: var(--lt-gray);
}
/* Firefox */
.range__input::-moz-range-thumb {
  background-color: var(--white);
  border: 0;
  border-radius: 50%;
  box-shadow: 0 0.125em 0.5em hsl(0,0%,0%,0.3);
  width: 1.5em;
  height: 1.5em;
  transition: background-color 0.15s linear;
}
.range__input:focus::-moz-range-thumb,
.range__input::-moz-range-thumb:hover {
  background-color: var(--lt-gray);
}
/* Continue main styles */
.range__output,
.range__output:after,
.range__output-value-track,
.range__output-values {
  position: absolute;
}
.range__output,
.range__output:after {
  transform: translateX(-50%);
}
.range__output {
  --percent: 50%;
  background-color: var(--primary3);
  border-radius: 0.25em;
  color: var(--white);
  padding: 0.25em;
  bottom: calc(100% + 0.5em);
  left: var(--percent);
  text-align: center;
  width: 2em;
  height: 2em;
  transition: background-color var(--trans-dur);
}
.range__output:after {
  border-top: 0.5em solid var(--primary3);
  border-left: 0.5em solid transparent;
  border-right: 0.5em solid transparent;
  content: "";
  display: block;
  top: calc(100% - 1px);
  left: 50%;
  width: 0;
  height: 0;
}
.range__output-value-track {
  inset: 0;
  overflow: hidden;
}
.range__output-values {
  --transX: 0;
  display: flex;
  align-items: center;
  white-space: nowrap;
  top: 0;
  left: 0;
  height: 100%;
  transform: translateX(var(--transX));
  transition: transform 0.15s linear;
}
.range__output-value {
  width: 2em;
}
/* `:focus-visible` support */
@supports selector(:focus-visible) {
  .range__input:focus::-webkit-slider-thumb {
    background-color: var(--white);
  }
  .range__input:focus-visible::-webkit-slider-thumb,
  .range__input::-webkit-slider-thumb:hover {
    background-color: var(--lt-gray);
  }
  .range__input:focus::-moz-range-thumb {
    background-color: var(--white);
  }
  .range__input:focus-visible::-moz-range-thumb,
  .range__input::-moz-range-thumb:hover {
    background-color: var(--lt-gray);
  }
}

3. Enable the range slider & sliding values.

window.addEventListener("DOMContentLoaded",() => {
  const fr = new RangeSlidingValue("example");
});
class RangeSlidingValue {
  constructor(id) {
    this.input = document.getElementById(id);
    this.output = document.querySelector('[data-tip]');
    this.values = this.output?.querySelector("[data-values]");
    this.init();
  }
  init() {
    this.input?.addEventListener("input",this.update.bind(this));
    this.populateValues();
    this.update();
  }
  populateValues() {
    const digitSpan = document.createElement("span");
    digitSpan.className = "range__output-value";
    const { min, max } = this.input;
    for (let v = min; v <= max; ++v) {
      const newSpan = digitSpan.cloneNode();
      newSpan.innerText = v;
      this.values?.appendChild(newSpan);
    }
  }
  update(e) {
    let value = this.input.defaultValue;
    // when manually set
    if (e) value = e.target?.value;
    // when initiated
    else this.input.value = value;
    const min = this.input.min || 0;
    const max = this.input.max || 100;
    const possibleValues = max - min;
    const relativeValue = (value - min) / possibleValues;
    const percentRaw = relativeValue * 100;
    const percent = +percentRaw.toFixed(2);
    const tipWidth = 2;
    const transXRaw = -tipWidth * relativeValue * possibleValues;
    const transX = +transXRaw.toFixed(2);
    const prop1 = "--percent";
    const prop2 = "--transX";
    this.input?.style.setProperty(prop1,`${percent}%`);
    this.output?.style.setProperty(prop1,`${percent}%`);
    this.values?.style.setProperty(prop2,`${transX}em`);
  }
}

You Might Be Interested In:


Leave a Reply