Powered by NeGD | MeitY Government of India® UX4G
Range
For built-in customization and consistent cross-browser appearance, use our custom range inputs.
Overview #
With .form-range
, developer can design own <input type="range">
controls. The thumb (the value) and track (the background) are both styled to look the same in all browsers. It is not supportable because only Firefox allows users to visibly show progress by "filling"
their track from either the left or right of the thumb.
<label for="customRange1" class="form-label">Example range</label>
<input type="range" class="form-range" id="customRange1"/>
RESULT
A double range slider is an interactive user interface element that enables users to select a range of values between defined minimum and maximum limits. By utilizing custom JavaScript, you can enhance its functionality, allowing for real-time updates and dynamic filtering of content based on the selected range.
<script>
window.addEventListener('DOMContentLoaded', () => {
const sliders = document.querySelectorAll(".dual-range-value, .dual-range-none-value");
sliders.forEach(slider => {
new dualRangeSlider(slider);
});
});
class dualRangeSlider {
constructor(rangeElement) {
this.range = rangeElement
this.min = Number(rangeElement.dataset.min)
this.max = Number(rangeElement.dataset.max)
this.handles = [...this.range.querySelectorAll(".handle")]
this.startPos = 0;
this.activeHandle;
this.handles.forEach(handle => {
handle.addEventListener("mousedown", this.startMove.bind(this))
handle.addEventListener("touchstart", this.startMoveTouch.bind(this))
})
window.addEventListener("mouseup", this.stopMove.bind(this))
window.addEventListener("touchend", this.stopMove.bind(this))
window.addEventListener("touchcancel", this.stopMove.bind(this))
window.addEventListener("touchleave", this.stopMove.bind(this))
const rangeRect = this.range.getBoundingClientRect();
const handleRect = this.handles[0].getBoundingClientRect()
this.range.style.setProperty("--x-1", "0px");
this.range.style.setProperty("--x-2", rangeRect.width - handleRect.width/2 + "px");
this.handles[0].dataset.value = this.range.dataset.min;
this.handles[1].dataset.value = this.range.dataset.max;
}
startMoveTouch(e) {
const handleRect = e.target.getBoundingClientRect()
this.startPos = e.touches[0].clientX - handleRect.x;
this.activeHandle = e.target;
this.moveTouchListener = this.moveTouch.bind(this)
window.addEventListener("touchmove", this.moveTouchListener);
}
startMove(e) {
this.startPos = e.offsetX;
this.activeHandle = e.target;
this.moveListener = this.move.bind(this)
window.addEventListener("mousemove", this.moveListener);
}
moveTouch(e) {
this.move({clientX: e.touches[0].clientX})
}
move(e) {
const isLeft = this.activeHandle.classList.contains("left")
const property = isLeft ? "--x-1" : "--x-2";
const parentRect = this.range.getBoundingClientRect();
const handleRect = this.activeHandle.getBoundingClientRect();
let newX = e.clientX - parentRect.x - this.startPos;
if(isLeft) {
const otherX = parseInt(this.range.style.getPropertyValue("--x-2"));
newX = Math.min(newX, otherX - handleRect.width)
newX = Math.max(newX, 0 - handleRect.width/2)
} else {
const otherX = parseInt(this.range.style.getPropertyValue("--x-1"));
newX = Math.max(newX, otherX + handleRect.width)
newX = Math.min(newX, parentRect.width - handleRect.width/2)
}
this.activeHandle.dataset.value = this.calcHandleValue((newX + handleRect.width/2) / parentRect.width)
this.range.style.setProperty(property, newX + "px");
}
calcHandleValue(percentage) {
return Math.round(percentage * (this.max - this.min) + this.min)
}
stopMove() {
window.removeEventListener("mousemove", this.moveListener);
window.removeEventListener("touchmove", this.moveTouchListener);
}
}
</script>
Add this custom JavaScript, select a range of values between defined minimum and maximum limits.
<div class="dual-range dual-range-value" data-min="0" data-max="100">
<span class="handle left"></span>
<span class="highlight"></span>
<span class="handle right"></span>
</div>
RESULT
<div class="dual-range dual-range-none-value" data-min="0" data-max="100">
<span class="handle left"></span>
<span class="highlight"></span>
<span class="handle right"></span>
</div>
RESULT
Disabled #
To give an input a grayed-out appearance, stop pointer
events, and prevent focusing, add the disabled
boolean attribute.
<label for="disabledRange" class="form-label">Disabled range</label> <input type="range" class="form-range" id="disabledRange" disabled>
RESULT
Min and max #
Range inputs come with implied minimum
and maximum
values of 0
and 100
, respectively. By using the min and max properties, it is possible to define new values for them.
<label for="customRange2" class="form-label">Example range</label> <input type="range" class="form-range" min="0" max="5" id="customRange2">
RESULT
Steps #
Range inputs "snap"
to integer values by default. You can alter this by specifying a step value. By using step="0.5"
in the example below, we double the number of steps.
<label for="customRange3" class="form-label">Example range</label>
<input type="range" class="form-range" min="0" max="5" step="0.5" id="customRange3"/>
RESULT
Sass #
Variables #
$form-range-track-width: 100%;
$form-range-track-height: .5rem;
$form-range-track-cursor: pointer;
$form-range-track-bg: $gray-300;
$form-range-track-border-radius: 1rem;
$form-range-track-box-shadow: $box-shadow-inset;
$form-range-thumb-width: 1rem;
$form-range-thumb-height: $form-range-thumb-width;
$form-range-thumb-bg: $component-active-bg;
$form-range-thumb-border: 0;
$form-range-thumb-border-radius: 1rem;
$form-range-thumb-box-shadow: 0 .1rem .25rem rgba($black, .1);
$form-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-focus-box-shadow;
$form-range-thumb-focus-box-shadow-width: $input-focus-width; // For focus box shadow issue in Edge
$form-range-thumb-active-bg: tint-color($component-active-bg, 70%);
$form-range-thumb-disabled-bg: $gray-500;
$form-range-thumb-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out;