I have a custom range slider.
The issue I'm running into, however, is that I can't manage to make the sliding of the custom “thumb” (in this case 🔥) work on touch devices.
Here's the project live: https://wagon-city-guides.herokuapp.com/spots/32
If you check it on your mobile (I use an iPhone), you'll still see the border of the original thumb (I left it on purpose for now), which slides and works, but the flame (the custom thumb) doesn’t come along … while it works fine for click devices.
I'm looking for a Vanilla JS solution only. 🙏🏽
Here's my code:
class RatingSlider {
constructor() {
this.ratingSliderForm = document.querySelector(".js-rating-slider-form");
this.ratingSliderInput = document.querySelector(".js-rating-slider-input");
this.ratingSliderThumb = document.querySelector(".js-rating-slider-thumb");
this.ratingSliderValue = document.querySelector(".js-rating-slider-value");
this.ratingSliderIcon = document.querySelector(".js-rating-slider-icon");
this.isPressed = false;
this.moveEvent;
this.holdEvent;
this.releaseEvent;
this.bind();
}
handleSliding(event) {
if (!this.isPressed) {
return;
}
if (
event.offsetX > 0 &&
event.offsetX < this.ratingSliderInput.offsetWidth
) {
this.ratingSliderThumb.style.left = `${event.offsetX - 10}px`;
this.ratingSliderIcon.style.transform = `scale(${1 +
this.ratingSliderInput.value / 150})`;
this.ratingSliderValue.innerText = `${this.ratingSliderInput.value}°`;
}
}
setRating() {
this.ratingSliderThumb.style.left = `${(this.ratingSliderInput.offsetWidth /
100) *
this.ratingSliderInput.value -
10}px`;
this.ratingSliderIcon.style.transform = `scale(${1 +
this.ratingSliderInput.value / 150})`;
this.ratingSliderValue.innerText = `${this.ratingSliderInput.value}°`;
this.ratingSliderInput.addEventListener(
`${this.holdEvent}`,
() => (this.isPressed = true)
);
this.ratingSliderInput.addEventListener(`${this.releaseEvent}`, () => {
this.isPressed = false;
this.ratingSliderForm.submit();
});
}
setEvents() {
if ("ontouchstart" in document.documentElement) {
this.moveEvent = "touchmove";
this.holdEvent = "touchstart";
this.releaseEvent = "touchend";
} else {
this.moveEvent = "mousemove";
this.holdEvent = "mousedown";
this.releaseEvent = "mouseup";
}
}
bind() {
if (!this.ratingSliderForm) {
return;
}
this.setEvents();
this.setRating();
this.ratingSliderInput.addEventListener(`${this.moveEvent}`, event =>
this.handleSliding(event)
);
}
}
export default RatingSlider;
The issue is that the touch events don't have offsetX
and offsetY
properties. Their values are returning undefined
in mobile devices. So, you need to add those to the event.
Right at the beginning of handleSliding(event)
method, add this:
if ("ontouchstart" in document.documentElement) {
event = addOffsetsOnTouch(event);
}
function addOffsetsOnTouch(e) {
let touch = e.touches[0] || event.changedTouches[0];
let target = document.elementFromPoint(touch.clientX, touch.clientY);
event.offsetX = touch.clientX - target.getBoundingClientRect().x;
event.offsetY = touch.clientY - target.getBoundingClientRect().y
return e;
}