Skip to content

Instantly share code, notes, and snippets.

@andrashann
Last active December 9, 2022 16:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrashann/aa38f8c718cdaf024284f65550dce885 to your computer and use it in GitHub Desktop.
Save andrashann/aa38f8c718cdaf024284f65550dce885 to your computer and use it in GitHub Desktop.

Range slider for Vue2

This is a component for Vue2 with two sliders that mark a from-to range. It is adapted to Vue from this article by Predrag Davidovic.

Since it was written for a Vue2 project, it doesn't directly support multiple v-model values, hence the emission of events on value changes – which then have to be processed by the parent component. I included ParentComponent.vue to show how to use this.

<template>
<div>
<div class="range_container">
<div class="sliders_control">
<input
id="fromSlider"
v-model="fromVal"
type="range"
:min="min"
:max="max"
/>
<input
id="toSlider"
v-model="toVal"
type="range"
:style="{
background: sliderBackgroundGradient,
zIndex: to <= 0 ? 2 : 0,
}"
:min="min"
:max="max"
/>
</div>
</div>
</div>
</template>
<script>
// https://medium.com/@predragdavidovic10/native-dual-range-slider-html-css-javascript-91e778134816
// https://codepen.io/predragdavidovic/pen/mdpMoWo
// TODO: in vue3, we can have a simpler multiple v-model setup instead of emitting events
export default {
props: {
from: { type: Number, default: 0 },
to: { type: Number, default: 100 },
min: { type: Number, default: 0 },
max: { type: Number, default: 100 },
sliderColor: { type: String, default: '#C6C6C6' },
rangeColor: { type: String, default: '#338CFF' },
},
data() {
return {
fromVal: null,
toVal: null,
}
},
computed: {
sliderBackgroundGradient() {
const rangeDistance = this.max - this.min
const fromPosition = this.fromVal - this.min
const toPosition = this.toVal - this.min
return `linear-gradient(
to right,
${this.sliderColor} 0%,
${this.sliderColor} ${(fromPosition / rangeDistance) * 100}%,
${this.rangeColor} ${(fromPosition / rangeDistance) * 100}%,
${this.rangeColor} ${(toPosition / rangeDistance) * 100}%,
${this.sliderColor} ${(toPosition / rangeDistance) * 100}%,
${this.sliderColor} 100%)`
},
},
watch: {
to(t) {
this.toVal = t
},
from(f) {
this.fromVal = f
},
fromVal(newFrom) {
if (Number(newFrom) > Number(this.toVal)) {
this.fromVal = Number(this.toVal)
}
this.fromVal = Number(this.fromVal)
this.$emit('fromChange', this.fromVal)
},
toVal(newTo) {
if (Number(newTo) < Number(this.fromVal)) {
this.toVal = Number(this.fromVal)
}
this.toVal = Number(this.toVal)
this.$emit('toChange', this.toVal)
},
},
created() {
this.fromVal = Math.max(this.from, this.min)
this.toVal = Math.min(this.to, this.max)
},
}
</script>
<style scoped>
.range_container {
height: 42px;
}
.sliders_control {
position: relative;
top: 20px;
min-height: 20px;
}
input[type='range']::-webkit-slider-thumb {
-webkit-appearance: none;
pointer-events: all;
width: 24px;
height: 24px;
background-color: #fff;
border-radius: 50%;
box-shadow: 0 0 0 1px #c6c6c6;
cursor: pointer;
}
input[type='range']::-moz-range-thumb {
-webkit-appearance: none;
pointer-events: all;
width: 24px;
height: 24px;
background-color: #fff;
border-radius: 50%;
box-shadow: 0 0 0 1px #c6c6c6;
cursor: pointer;
}
input[type='range']::-webkit-slider-thumb:hover {
background: #f7f7f7;
}
input[type='range']::-webkit-slider-thumb:active {
box-shadow: inset 0 0 3px #387bbe, 0 0 9px #387bbe;
-webkit-box-shadow: inset 0 0 3px #387bbe, 0 0 9px #387bbe;
}
input[type='range'] {
-webkit-appearance: none;
appearance: none;
height: 3px;
width: 100%;
position: absolute;
background-color: #c6c6c6;
pointer-events: none;
}
#fromSlider {
height: 0;
z-index: 1;
}
</style>
<template>
<div>
<range-slider
:to="to"
:from="from"
@toChange="to = $event"
@fromChange="from = $event"
></range-slider>
{{ from }}
{{ to }}
</div>
</template>
<script>
import RangeSlider from '~/components/RangeSlider.vue'
export default {
components: { RangeSlider },
data() {
return { from: 20, to: 80 }
},
}
</script>
<style></style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment