3

I'm having issue with creating loop inside carousel so it will go back to first card after reaching last one on a click event - rightButton. So far carousel stops when reach last card.

const carousel = document.querySelector("[data-target='carousel']");
const card = carousel.querySelector("[data-target='card']");
const leftButton = document.querySelector("[data-action='slideLeft']");
const rightButton = document.querySelector("[data-action='slideRight']");

const carouselWidth = carousel.offsetWidth;
const cardStyle = card.currentStyle || window.getComputedStyle(card)
const cardMarginRight = Number(cardStyle.marginRight.match(/\d+/g)[0]);

const cardCount = carousel.querySelectorAll("[data-target='card']").length;

let offset = 0;
const maxX = -((cardCount / 3) * carouselWidth + 
               (cardMarginRight * (cardCount / 3)) - 
               carouselWidth - cardMarginRight);


leftButton.addEventListener("click", function() {
  if (offset !== 0) {
    offset += carouselWidth + cardMarginRight;
    carousel.style.transform = `translateX(${offset}px)`;
    }
})
  
rightButton.addEventListener("click", function() {
  if (offset !== maxX) {
    offset -= carouselWidth + cardMarginRight;
    carousel.style.transform = `translateX(${offset}px)`;
  }
})
.wrapper {
  height: 100px;
  width: 432px;
  position: relative;
  overflow: hidden;
  margin: 0 auto;
}

.button-wrapper {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  position: absolute;
}

.carousel {
  margin: 0;
  padding: 0;
  list-style: none;
  width: 100%;
  display: flex;
  position: absolute;
  left: 0;
  transition: all 1s ease;
}

.card {
  background: black;
  min-width: 100px;
  height: 100px;
  margin-right: 1rem;
  display: inline-block;
}

span {
  color:#ffffff;
}
<div class="wrapper">
  <ul class="carousel" data-target="carousel">
    <li class="card" data-target="card"><span>1</span></li>
    <li class="card" data-target="card"><span>2</span></li>
    <li class="card" data-target="card"><span>3</span></li>
    <li class="card" data-target="card"><span>4</span></li>
    <li class="card" data-target="card"><span>5</span></li>
    <li class="card" data-target="card"><span>6</span></li>
    <li class="card" data-target="card"><span>7</span></li>
    <li class="card" data-target="card"><span>8</span></li>
    <li class="card" data-target="card"><span>9</span></li>
  </ul>
  <div class="button-wrapper">
    <button data-action="slideLeft">L</button>
    <button data-action="slideRight">R</button>
  </div>
</div>

Code available on jsfiddle: https://jsfiddle.net/2qv6mpb1/

Is there a chance that someone could point me in a proper direction on how to achieve that? I

ChrisCoz
  • 63
  • 2

2 Answers2

0

You need to handle when your offset is equal to the maxX, and reset the offset back to zero.

rightButton.addEventListener("click", function() {
  if (offset !== maxX) {
    offset -= carouselWidth + cardMarginRight;
    carousel.style.transform = `translateX(${offset}px)`;
  } else {
    offset = 0;
    carousel.style.transform = `translateX(${offset}px)`;
  }
})
ZephDavies
  • 3,964
  • 2
  • 14
  • 19
  • This will still fail if one resizes the browser and than operates with the carousel. – Roko C. Buljan Apr 16 '21 at 14:24
  • Ideally, it should use an index, rather than a pixel offset, for this purpose. – ZephDavies Apr 16 '21 at 14:25
  • @ZephDavies in that case what would you suggest? As I tried your solution and worked only when 9 items. If there is more it doesn't work anymore. Even if items wrapper will be bigger or smaller and main list element is set to 100% width. – ChrisCoz Apr 16 '21 at 14:45
0

This will use fixed widths with a gap of 10px (see CSS) - (to make it responsive you should modify the px used in JS to translate in % steps).
Also, it will work for any number of .Carousel elements on the page.
Also, simplify the HTML markup as per below, which is more consistent with the CSS for a better modular methodology

const Carousel = (EL) => {
  const CARDS = EL.querySelector(".Carousel-cards");
  const PREV = EL.querySelector(".Carousel-prev");
  const NEXT = EL.querySelector(".Carousel-next");
  const w = EL.offsetWidth;
  const d = CARDS.offsetWidth - w; // widths diff
  let x = 0;
  
  const anim = (dir) => {
    x += w * dir;
    x = Math.min(d, Math.max(0, x));
    CARDS.style.transform = `translateX(-${x}px)`;
  };
  
  PREV.addEventListener("click", () => anim(-1)) 
  NEXT.addEventListener("click", () => anim(+1))
};
document.querySelectorAll(".Carousel").forEach(Carousel);
.Carousel {
  height: 100px;
  width: 430px;  /*  (100px * 4) + (10px * 3gap)  */
  position: relative;
  overflow: hidden;
  margin: 0 auto;
}

.Carousel-nav {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  position: absolute;
}

.Carousel-cards {
  position: absolute;
  left: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  transition: transform 1s ease;
  gap: 10px;
}

.Carousel-cards > * {
  background: black;
  min-width: 100px;
  height: 100px;
}

span {
  color: #ffffff;
}
<div class="Carousel">
  <ul class="Carousel-cards">
    <li><span>1</span></li>
    <li><span>2</span></li>
    <li><span>3</span></li>
    <li><span>4</span></li>
    <li><span>5</span></li>
    <li><span>6</span></li>
    <li><span>7</span></li>
    <li><span>8</span></li>
    <li><span>9</span></li>
  </ul>
  <div class="Carousel-nav">
    <button class="Carousel-prev">L</button>
    <button class="Carousel-next">R</button>
  </div>
</div>

There's more to improve, i.e: makes no sense to have buttons if the content does not require animating, or one of the buttons depending if a direction is completed.

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • Thanks for your help but don't quite understand it. As I tried to match it with my code and style but it doesn't work. At that time I found even your solution stops at the last one. Also, about this part: "px used in JS to translate in % step" What do you mean by that? This should refer to offsetWidth? Or more items wrapper in CSS [which I presume is that one?] – ChrisCoz Apr 16 '21 at 15:17