skip to Main Content

I created a carousel slider similar to the one in UIkit slider, I want to use this function to multiple carousel in the same html page but it’s not working as expected because I am using this line of code which query single carousel only:

const carousel = document.querySelector(".c-carousel-inner");

I’ve tried using this but apparently on button press it will trigger the other carousel also

const carousels = document.querySelectorAll(".c-carousel-inner");
carousels.forEach((carousel) => {
// code here
});

I’ve seen some solution which uses constructor, but I’m not sure if that’s the best approach here

Here’s my full javascript code without any applied code from above

const carousel = document.querySelector(".c-carousel-inner");
const carouselLength = carousel.children.length;
const carouselItemWidth = document.querySelector(".c-carousel-inner .c-carousel-item").offsetWidth;
const carouselButtons = document.querySelectorAll(".c-carousel-control");
let startX, scrollSum = 0, mouseIsDown, count = 0, mx, moving=false, lastDirection, prevScroll;

carouselButtons.forEach((button) => {
    button.addEventListener("click", function(){
        if (button.id == "carousel-right") {
            if (prevScroll) {
                mouseToLeft();
            }
            prevScroll = true;
            moveToLeftSnap();

        } else if (button.id == "carousel-left") {
            if (!prevScroll) {
                mouseToRight();
            }
            moveToRightSnap();
            prevScroll = false;
        }
    });
})

function mouseToLeft() {
    if (count >= carouselLength) {
        count = 0;
    }
    count++;
    for (i=0; i < carouselLength; i++) {
        carousel.children[i].style.removeProperty("order");
        if (i < count) {
            carousel.children[i].style.order = "1";
        }
    }
}

function mouseToRight() {
    if (count <= 0) {
        count = carouselLength;
    }
    count--;    
    for (i=carouselLength-1; i>=0; i--) {
        carousel.children[i].style.removeProperty("order");
        if (i >= count) {
            carousel.children[i].style.order = "-1";
        }        
    }
}

function moveToRightSnap(value=carouselItemWidth) {
    carousel.style.removeProperty("transition");
    carousel.style.transform = `translate3d(-${value}px, 0, 0)`;
    setTimeout(() => {
        carousel.style.transition = `transform 0.342s ease`;
        carousel.style.transform = `translate3d(0, 0, 0)`;
    }, 1); 
}

function moveToLeftSnap(value=0) {
    carousel.style.removeProperty("transition");
    carousel.style.transform = `translate3d(${value}, 0, 0)`;
    setTimeout(() => {
        carousel.style.transition = `transform 0.342s ease`;
        carousel.style.transform = `translate3d(-${carouselItemWidth}px, 0, 0)`;
    }, 1);
}

const startDrag = (e) => {
    mouseIsDown = true;
    startX = e.pageX;
    mx = e.pageX;
    carousel.style.removeProperty("transition");
}

const dragging = (e) => {
    if (mouseIsDown) {
        scrollSum = e.pageX - startX;
        console.log(scrollSum);
        if (mx < e.pageX) {
            lastDirection = "right";
        } else if (mx > e.pageX) {
            lastDirection = "left"
        }
        if (scrollSum > 0) {
            startX = e.pageX + carouselItemWidth;
            scrollSum = scrollSum - carouselItemWidth;
            if (!prevScroll) {
                mouseToRight();
            }
        } else if (prevScroll || scrollSum <= -carouselItemWidth) {
            scrollSum = 0;
            startX = e.pageX;
            mouseToLeft();
            prevScroll = false;
        }
        carousel.style.transform = `translate3d(${scrollSum}px,0,0)`;
        mx = e.pageX;
    }
} 

const stopDrag = () => {
    mouseIsDown = false;
    if (lastDirection == "right") {
        moveToRightSnap(scrollSum);
    } else if (lastDirection == "left") {
        moveToLeftSnap(scrollSum);
        prevScroll = true;
    }
}

carousel.addEventListener("mousedown", startDrag);

document.addEventListener("mousemove", dragging);

document.addEventListener("mouseup", stopDrag);

HTML code currently only single carousel is added here to make it short

<div class="main-container">
  <div class="container">
    <div class="c-carousel">
      <ul id="" class="c-carousel-inner d-flex">
        <% topAiring.forEach((item, index) => { %>
        <li class="c-carousel-item item-<%= index + 1 %> col-3 <%= index == 0 ? 'active' : '' %>">
          <img src="<%= item.images.jpg['image_url'] %>" draggable="false" class="d-block w-100" alt="...">
        </li>
        <% }) %>
      </ul>
      <button id="carousel-left" class="c-carousel-control" type="button">
        <span class="carousel-control-prev-icon" aria-hidden="true"></span>
        <span class="visually-hidden">Previous</span>
      </button>
      <button id="carousel-right" class="c-carousel-control" type="button">
        <span class="carousel-control-next-icon" aria-hidden="true"></span>
        <span class="visually-hidden">Next</span>
      </button>
    </div>
  </div>
</div>

2

Answers


  1. This code as it is has some fundamental issues. Most noticeably, in the case of multiple carousels you will get duplicated ids. id should be a unique identifier for any element.
    Instead of id, you can use data attribute on your buttons. For example:

    <div class="main-container">
      <div class="container">
        <div class="c-carousel">
          <ul id="carousel_01" class="c-carousel-inner d-flex">
            <% topAiring.forEach((item, index) => { %>
            <li class="c-carousel-item item-<%= index + 1 %> col-3 <%= index == 0 ? 'active' : '' %>">
              <img src="<%= item.images.jpg['image_url'] %>" draggable="false" class="d-block w-100" alt="...">
            </li>
            <% }) %>
          </ul>
          <button id="l_01" data-direction="left" class="c-carousel-control" type="button">
            <span class="carousel-control-prev-icon" aria-hidden="true"></span>
            <span class="visually-hidden">Previous</span>
          </button>
          <button id="r_01" data-direction="right" class="c-carousel-control" type="button">
            <span class="carousel-control-next-icon" aria-hidden="true"></span>
            <span class="visually-hidden">Next</span>
          </button>
        </div>
      </div>
    </div>
    

    Your function will then become something like:

    carouselButtons.forEach((button) => {
        button.addEventListener("click", function(){
            const btnIdTail = button.id.split("_")[1];
            const carousel = document.getElementById(`carousel_${btnIdTail}`);
            const btnDir = button.dataset.direction;
            if (btnDir == "right") {
                if (prevScroll) {
                    mouseToLeft(carousel);
                }
                prevScroll = true;
                moveToLeftSnap(carousel);
    
            } else if (btnDir == "left") {
                if (!prevScroll) {
                    mouseToRight(carousel);
                }
                moveToRightSnap(carousel);
                prevScroll = false;
            }
        });
    })
    

    Get the carousel by their id based on the button id. In your handler functions use the specific carousel which you pass in, like:

    function mouseToLeft(carousel) {
        const carouselLength = carousel.children.length;
        if (count >= carouselLength) {
            count = 0;
        }
        count++;
        for (i=0; i < carouselLength; i++) {
            carousel.children[i].style.removeProperty("order");
            if (i < count) {
                carousel.children[i].style.order = "1";
            }
        }
    }
    function moveToRightSnap(carousel, value=carouselItemWidth) {
        //...
    }
    
    function moveToLeftSnap(carousel, value=0) {
        //...
    }
    

    and do the same for other functions.
    Don’t forget to id your carousels with the same kind of numbering you used for its button like:

    <ul id="carousel_01" class="c-carousel-inner d-flex">
    

    To implement dragging handles, get the target carousel from the event and pass it on to the functions as above. Like:

    const startDrag = (e) => {
        // get the carousel
        const carousel = e.target;
        mouseIsDown = true;
        startX = e.pageX;
        mx = e.pageX;
        carousel.style.removeProperty("transition");
    }
    
    const dragging = (e) => {
        // get the carousel, use and pass it
        const carousel = e.target;
        const carouselItemWidth = carousel.offsetWidth;
        if (mouseIsDown) {
            scrollSum = e.pageX - startX;
            console.log(scrollSum);
            if (mx < e.pageX) {
                lastDirection = "right";
            } else if (mx > e.pageX) {
                lastDirection = "left"
            }
            if (scrollSum > 0) {
                startX = e.pageX + carouselItemWidth;
                scrollSum = scrollSum - carouselItemWidth;
                if (!prevScroll) {
                    mouseToRight(carousel);
                }
            } else if (prevScroll || scrollSum <= -carouselItemWidth) {
                scrollSum = 0;
                startX = e.pageX;
                mouseToLeft(carousel);
                prevScroll = false;
            }
            carousel.style.transform = `translate3d(${scrollSum}px,0,0)`;
            mx = e.pageX;
        }
    } 
    
    const stopDrag = (e) => {
        const carousel = e.target;
        mouseIsDown = false;
        if (lastDirection == "right") {
            moveToRightSnap(carousel, scrollSum);
        } else if (lastDirection == "left") {
            moveToLeftSnap(carousel, scrollSum);
            prevScroll = true;
        }
    }
    

    You will need to localise scrollSum and other variables that can impact other carousels. I will leave that to you

    Alternatively, you can make use of event bubbling that will reduce the number of listeners you create to, but that would be a whole different approach than what you currently have.

    Login or Signup to reply.
  2. If you know which carousel is clicked or draged, you can do it easily. right?

    -javascript

    let carousel = document.createElement('div');
    const carousels = document.querySelectorAll(".c-carousel");
    carousels.forEach((e) => {
      e.addEventListener("mousedown", () => {
        carousel = e.children[0];  //get .c-carousel-inner from .c-carousel
      });
    });
    //when you mouse down in any carousel (including button in it) this'll works and variable 'carousel' will be clicked carousel. ok?
     
    ...
    ...
    
    
    carouselButtons.forEach((button) => {
      button.addEventListener("mouseup", function () {
        //I change click to mouseup because handling mousedown event on carousel must be first and then handle button event.
    
        if (button.carousel-direction == "right") {
          if (prevScroll) {
            mouseToLeft();
          }
          prevScroll = true;
          moveToLeftSnap();
        } else if (button.carousel-direction == "left") {
          if (!prevScroll) {
            mouseToRight();
          }
          moveToRightSnap();
          prevScroll = false;
        }
      });
    });
    
        
    ...
    ...
    

    -html

      <button id="l_01" carousel-direction="left" class="c-carousel-control" type="button">
        <span class="carousel-control-prev-icon" aria-hidden="true">. 
        </span>
        <span class="visually-hidden">Previous</span>
      </button>
      <button id="r_01" carousel-direction="right" class="c-carousel-control" type="button">
        <span class="carousel-control-next-icon" aria-hidden="true">. 
        </span>
        <span class="visually-hidden">Next</span>
      </button>
    

    I hope this answer can help you.

    Good luck.

    Oh, don’t forget to vote my answer if it works.

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