skip to Main Content

I am creating a store and on my landing page I want to create a touch-enabled slider/carousel with products (like the one on mobile https://www.nike.com/es).

I have created an overflowing container (.slider-container) with 6 "product-slots" (.slide) and I have set overflow to scroll in css. Now I want that whenever someone slides (touchmove) a bit, it snaps to the next product once released (touchend)

I have tried something in JS but I am an total amateur… (I don’t want to use SliderJs, etc. – trying to do it myself and learn a bit)

Here is HTML and CSS:

This is what I have tried on JS and basically, whenever you try to scroll, you get sent back to the beginning (slider.scrollLeft = 0)

let startPos = 0
let sliderScroll = 0
let prevPos = 0
let currentPos = 0

const slider = document.querySelector('.slider-container')
const slideWidth = document.querySelector('.slide').getBoundingClientRect().width

function getPositionX(event) {
    return
    event.touches[0].clientX
}

function touchStart(event) {
    startPos = getPositionX(event)
    sliderScroll = slider.scrollLeft
}

function touchMove(event) {
    currentPos = getPositionX(event)
}

function touchEnd() {
    let calcNumber = Math.round((sliderScroll + Math.abs(startPos - currentPos)) / slideWidth)
    let setScrollPos = calcNumber * slideWidth
    
    
    slider.scrollTo({
        left: setScrollPos,
        behaviour: 'smooth'
    })

}

slider.addEventListener('touchstart', touchStart)
slider.addEventListener('touchmove', touchMove)
slider.addEventListener('touchend', touchEnd)
*{
  margin: 0;
  padding: 0;
  font-family: "Roboto Condensed", sans-serif;
}


html{
   scroll-behavior:smooth;
   overflow-x: hidden;
}

body {
   overflow-x: hidden;
}

.slider-container{
  display: flex;
  overflow: scroll;
}

.slide{
  min-width: 275px;
  margin: 20px 0 40px 0;
  padding-left: 10px;

}

.preview-product-title{
font-size: 17px;
padding: 2px 0 2px 0;
}

.preview-color{
  font-size: 17px;
  color: gray;
  padding-bottom: 2px;


}


.preview-price{
  font-size: 17px;
  font-weight: bold;


}



.slide:first-child{
  margin: 20px 0 40px 15px;
}

.slide:last-child{
  margin: 20px 20px 40px 0;
}
<div class="slider-container">

        <div class="slide">

            <img src="https://picsum.photos/id/237/200/300" style="width: 275px;">

            <p class="preview-product-title">Cool Shorts</p>
            <p class="preview-color">Blue Navy</p>
            <p class="preview-price">€29</p>
        </div>
        <div class="slide">

            <img src="https://picsum.photos/id/25/200/300" style="width: 275px;">

            <p class="preview-product-title">Cool Shorts</p>
            <p class="preview-color">Blue Navy</p>
            <p class="preview-price">€29</p>
        </div>
        <div class="slide">

            <img src="https://picsum.photos/id/9/200/300" style="width: 275px;">

            <p class="preview-product-title">Cool Shorts</p>
            <p class="preview-color">Blue Navy</p>
            <p class="preview-price">€29</p>
        </div>
        <div class="slide">

            <img src="https://picsum.photos/id/237/200/300" style="width: 275px;">

            <p class="preview-product-title">Cool Shorts</p>
            <p class="preview-color">Blue Navy</p>
            <p class="preview-price">€29</p>
        </div>
        <div class="slide">

            <img src="https://picsum.photos/id/13/200/300" style="width: 275px;">

            <p class="preview-product-title">Cool Shorts</p>
            <p class="preview-color">Blue Navy</p>
            <p class="preview-price">€29</p>
        </div>
        <div class="slide">

            <img src="https://picsum.photos/id/10/200/300" style="width: 275px;">

            <p class="preview-product-title">Cool Shorts</p>
            <p class="preview-color">Blue Navy</p>
            <p class="preview-price">€29</p>
        </div>

    </div>

2

Answers


  1. To snap the touch slider to the scroll position using JavaScript, you can make the following adjustments to your code:

    1. Update the getPositionX function to correctly return the clientX position.
    2. Calculate the distance moved correctly in the touchMove function.
    3. Use scrollLeft property instead of scrollTo method to set the scroll position.

    Here is the modified JavaScript code:

    let startPos = 0;
    let sliderScroll = 0;
    let prevPos = 0;
    let currentPos = 0;
    
    const slider = document.querySelector('.slider-container');
    const slideWidth = document.querySelector('.slide').getBoundingClientRect().width;
    
    function getPositionX(event) {
        return event.touches[0].clientX;
    }
    
    function touchStart(event) {
        startPos = getPositionX(event);
        sliderScroll = slider.scrollLeft;
    }
    
    function touchMove(event) {
        currentPos = getPositionX(event);
    }
    
    function touchEnd() {
        let distanceMoved = currentPos - startPos;
        let slideIndex = Math.round(distanceMoved / slideWidth);
        let setScrollPos = slideIndex * slideWidth;
    
        slider.scrollLeft = setScrollPos;
    }
    
    slider.addEventListener('touchstart', touchStart);
    slider.addEventListener('touchmove', touchMove);
    slider.addEventListener('touchend', touchEnd);
    

    These modifications should help in achieving the snapping behavior you desire for your touch slider/carousel.

    Login or Signup to reply.
  2. Your basic problem is that you have a return after your return:

    function getPositionX(event) {
        return
        event.touches[0].clientX
    }
    

    This will return undefined. Correct is:

    function getPositionX(event) {
        return event.touches[0].clientX
    }
    

    The next problem is then that you can’t scroll back. You need to remove the Math.abs:

        let calcNumber = Math.round((sliderScroll + startPos - currentPos) / slideWidth)
    

    Then you will find out that scrolling with touch has this inertia behaviour so it jumps but will keep scroll if you drag with enough speed. So it’s better to not use scrolling and scroll manually in your touch events and also mouse events. Also smooth scrolling with behaviour doesn’t work that well, but you can use the css setting scroll-behavior. But only while you’re not dragging.

    Complete example:

    let startPos = 0
    let sliderScroll = 0
    let prevPos = 0
    let currentPos = 0
    
    const slider = document.querySelector('.slider-container')
    const slideWidth = document.querySelector('.slide').getBoundingClientRect().width
    
    function getPositionX(event) {
        return event.touches[0].clientX
    }
    
    function touchStart(event) {
        startPos = getPositionX(event)
        sliderScroll = slider.scrollLeft
        slider.classList.remove("smoothscroll")
    }
    
    function touchMove(event) {
        currentPos = getPositionX(event)
        slider.scrollLeft = sliderScroll + startPos - currentPos
    }
    
    function touchEnd() {
        let calcNumber = Math.round((sliderScroll + startPos - currentPos) / slideWidth)
        let setScrollPos = calcNumber * slideWidth
        
        slider.classList.add("smoothscroll")
        slider.scrollTo(setScrollPos, 0)
    }
    
    let movable = false
    function mouseDown(event) {
        startPos = event.x
        sliderScroll = slider.scrollLeft
        slider.classList.remove("smoothscroll")
        movable = true
    }
    
    function mouseMove(event) {
        if(!movable)
            return
        currentPos = event.x
        slider.scrollLeft = sliderScroll + startPos - currentPos
    }
    
    function mouseUp() {
        movable = false
        let calcNumber = Math.round((sliderScroll + startPos - currentPos) / slideWidth)
        let setScrollPos = calcNumber * slideWidth
        
        slider.classList.add("smoothscroll")
        slider.scrollTo(setScrollPos, 0)
    
    }
    
    slider.addEventListener('touchstart', touchStart)
    slider.addEventListener('touchmove', touchMove)
    slider.addEventListener('touchend', touchEnd)
    slider.addEventListener('mousedown', mouseDown)
    slider.addEventListener('mousemove', mouseMove)
    slider.addEventListener('mouseup', mouseUp)
    *{
      margin: 0;
      padding: 0;
      font-family: "Roboto Condensed", sans-serif;
    }
    
    
    html{
       scroll-behavior:smooth;
       overflow-x: hidden;
    }
    
    body {
       overflow-x: hidden;
    }
    
    .slider-container{
      display: flex;
      overflow: hidden;
      width:500px;
    }
    
    .smoothscroll {
        scroll-behavior: smooth;
    }
    
    .slide{
      min-width: 275px;
      margin: 20px 0 40px 0;
      padding-left: 10px;
    
    }
    
    .preview-product-title{
      font-size: 17px;
      padding: 2px 0 2px 0;
      user-select: none;
    }
    
    .preview-color{
      font-size: 17px;
      color: gray;
      padding-bottom: 2px;
    }
    
    .preview-price{
      font-size: 17px;
      font-weight: bold;
    }
    
    .slide:first-child{
      margin: 20px 0 40px 15px;
    }
    
    .slide:last-child{
      margin: 20px 20px 40px 0;
    }
    <div class="slider-container">
            <div class="slide">
    
                <img src="https://picsum.photos/id/237/200/300" style="width: 275px;" draggable="false">
    
                <p class="preview-product-title">Cool Shorts</p>
                <p class="preview-color">Blue Navy</p>
                <p class="preview-price">€29</p>
            </div>
            <div class="slide">
    
                <img src="https://picsum.photos/id/25/200/300" style="width: 275px;" draggable="false">
    
                <p class="preview-product-title">Cool Shorts</p>
                <p class="preview-color">Blue Navy</p>
                <p class="preview-price">€29</p>
            </div>
            <div class="slide">
    
                <img src="https://picsum.photos/id/9/200/300" style="width: 275px;" draggable="false">
    
                <p class="preview-product-title">Cool Shorts</p>
                <p class="preview-color">Blue Navy</p>
                <p class="preview-price">€29</p>
            </div>
            <div class="slide">
    
                <img src="https://picsum.photos/id/237/200/300" style="width: 275px;" draggable="false">
    
                <p class="preview-product-title">Cool Shorts</p>
                <p class="preview-color">Blue Navy</p>
                <p class="preview-price">€29</p>
            </div>
            <div class="slide">
    
                <img src="https://picsum.photos/id/13/200/300" style="width: 275px;" draggable="false">
    
                <p class="preview-product-title">Cool Shorts</p>
                <p class="preview-color">Blue Navy</p>
                <p class="preview-price">€29</p>
            </div>
            <div class="slide">
    
                <img src="https://picsum.photos/id/10/200/300" style="width: 275px;" draggable="false">
    
                <p class="preview-product-title">Cool Shorts</p>
                <p class="preview-color">Blue Navy</p>
                <p class="preview-price">€29</p>
            </div>
    
        </div>

    I didn’t add inertia handling, that would be required or it feels a bit off and not as expected when you drag it with speed.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search