Home > Mobile >  Carousel getting blank when using translateX in forEach loop with querySeletorAll
Carousel getting blank when using translateX in forEach loop with querySeletorAll

Time:07-14

I created a carousel and two carousels run on the page, first carousel working perfect, but the second one is blank because of incrementing the translateX value when using the forEach loop with querySelectorAll.

Also when I click on the last item in carousel it moves to the other carousel item.

I'm not getting why it's happening.

Here is the demo: https://jsfiddle.net/Devbuddy/wm4h6Lr0/17/

JS:

const carouselContainer = document.querySelectorAll('.carousel-container');
const carouselBtns = document.createElement('div');
carouselBtns.innerHTML = '<button ><span>Prev</span></button><button ><span>Next</span></button>';

for (let i = 0; i < carouselContainer.length; i  ) {
    carouselContainer[i].appendChild(carouselBtns.cloneNode(true));
}



const slides = document.querySelectorAll(".carousel .image");



slides.forEach((slide, index) => {
  slide.style.transform = `translateX(${index * 100}%)`;
});

let currentSlide = 0;
let maxSlide = slides.length - 1;

const nextSlide = document.querySelector(".btn-carousel-next");

nextSlide.addEventListener("click", function(){

  if(currentSlide === maxSlide){
    currentSlide = 0;
  } else {
    currentSlide  
  }
  
  slides.forEach((slide, index)=>{
    slide.style.transform = `translateX(${100 * (index - currentSlide)}%)`;
  });
});

const prevSlide = document.querySelector(".btn-carousel-prev");

prevSlide.addEventListener("click", function(){
  if(currentSlide === 0){
    currentSlide = maxSlide
  } else {
    currentSlide--;
  }

  slides.forEach((slide, index)=>{
    slide.style.transform = `translateX(${100 * (index - currentSlide)}%)`;
  });
});

HTML:

<div >
<div >
<div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image1" alt=""></div>
<div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image2" alt=""></div>
<div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image3" alt=""></div>
<div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image4" alt=""></div>
<div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image5" alt=""></div>
<div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image6" alt=""></div>
</div>
</div>

<div >
<div >
<div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image7" alt=""></div>
<div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image8" alt=""></div>
<div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image9" alt=""></div>
<div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image10" alt=""></div>
<div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image11" alt=""></div>
<div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image12" alt=""></div>
</div>
</div>

css:

.carousel-container {
      position: relative;
      flex-grow: 1;
    }
    
    .carousel {
      width: 100%;
      max-width: 800px;
      height: 350px;
      position: relative;
      overflow: hidden;
    }
    
    .carousel div.image {
      width: 100% !important;
      position: absolute !important;
      transition: all 0.5s;
      max-width: 800px;
      height: 350px;
    }
    
    .carousel div.image img {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
    
    .carousel-container .btn-carousel {
      position: absolute;
      height: 40px;
      padding: 10px;
      border: none;
      z-index: 10px;
      cursor: pointer;
      background-color: #fff;
      font-size: 18px;
      display: block;
    }
    
    .carousel-container .btn-carousel-prev {
      top: 45%;
      left: 2%;
    }
    
    .carousel-container .btn-carousel-prev i::before {
      vertical-align: baseline;
    }
    
    .carousel-container .btn-carousel-next {
      top: 45%;
      right: 2%;
    }
    
    .carousel-container .btn-carousel-next i::before {
      vertical-align: baseline;
    }

CodePudding user response:

  • Your logic should be separated for every carousel
  • Animate the .carousel instead of every .image
  • Use CSS flex

// DOM utility functions:

const el = (sel, par) => (par || document).querySelector(sel);
const els = (sel, par) => (par || document).querySelectorAll(sel);
const elNew = (tag, prop) => Object.assign(document.createElement(tag), prop);

// Helper functions:

const mod = (n, m) => (n % m   m) % m;

// Task: Carousel:

const carousel = (elContainer) => {

  const elCarousel = el(".carousel", elContainer);
  const elsSlides = els(".image", elCarousel);
  let max = elsSlides.length;
  let c = 0; // Current slide index

  // Methods:
  const anim = () => elCarousel.style.transform = `translateX(${-c * 100}%)`;
  const prev = () => {
    c = mod(c-1, max);
    anim();
  };
  const next = () => {
    c = mod(c 1, max);
    anim();
  };
  
  // Buttons:
  
  const elPrev = elNew("button", {
    className: "btn-carousel btn-carousel-prev",
    innerHTML: "<span>Prev</span>",
    onclick: prev
  });
  
  const elNext = elNew("button", {
    className: "btn-carousel btn-carousel-next",
    innerHTML: "<span>Next</span>",
    onclick: next
  });
  
  elContainer.append(elPrev, elNext);
};

// Init:
els(".carousel-container").forEach(carousel);
.carousel-container {
  position: relative;
  flex-grow: 1;
  overflow: hidden;
}

.carousel {
  display: flex;
  transition: 0.3s; /* transition the parent instead */
}

.carousel .image {
  flex: 1 0 100%;
}

.carousel .image img {
  display: block;
  width: 100%;
  height: 350px;
  object-fit: cover;
}

.carousel-container .btn-carousel {
  position: absolute;
  height: 40px;
  padding: 10px;
  border: none;
  z-index: 10px;
  cursor: pointer;
  background-color: #fff;
  font-size: 18px;
  display: block;
}

.carousel-container .btn-carousel-prev {
  top: 45%;
  left: 2%;
}

.carousel-container .btn-carousel-prev i::before {
  vertical-align: baseline;
}

.carousel-container .btn-carousel-next {
  top: 45%;
  right: 2%;
}

.carousel-container .btn-carousel-next i::before {
  vertical-align: baseline;
}
<div >
  <div >
    <div ><img loading="lazy" src="https://via.placeholder.com/800x350/0bf?text=image1" alt=""></div>
    <div ><img loading="lazy" src="https://via.placeholder.com/800x350/fb0?text=image2" alt=""></div>
    <div ><img loading="lazy" src="https://via.placeholder.com/800x350/b0f?text=image3" alt=""></div>
    <div ><img loading="lazy" src="https://via.placeholder.com/800x350/0bf?text=image4" alt=""></div>
    <div ><img loading="lazy" src="https://via.placeholder.com/800x350/0fb?text=image5" alt=""></div>
    <div ><img loading="lazy" src="https://via.placeholder.com/800x350/f0b?text=image6" alt=""></div>
  </div>
</div>

<div >
  <div >
    <div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image7" alt=""></div>
    <div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image8" alt=""></div>
    <div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image9" alt=""></div>
    <div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image10" alt=""></div>
    <div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image11" alt=""></div>
    <div ><img loading="lazy" src="https://via.placeholder.com/800x350?text=image12" alt=""></div>
  </div>
</div>

CodePudding user response:

Two things. Firstly you need to target each slide separately, this is done using forEach carouselContainer. Secondly you need to scope your variables to the relevant slider, you can do this using querySelector as well but from the parent element and not the document, so still within the forEach loop of the slide containers you can then specify const slides = Array.from(carousel.querySelectorAll('.image');

I changed up the functionality a little bit to only slide .carousel instead of adding a transform to each slide. This, combined with the display: grid and display: flex should make the transitions more performant for the browser to calculate

const carouselContainer = Array.from(document.getElementsByClassName('carousel-container'));

const carouselBtns = document.createElement('div');
carouselBtns.classList.add('buttons');
carouselBtns.innerHTML = '<button ><span>Prev</span></button><button ><span>Next</span></button>';

carouselContainer.forEach((carousel, index) => {
  const slidesHolder = carousel.firstElementChild;
  carousel.appendChild(carouselBtns.cloneNode(true));

  const slides = Array.from(slidesHolder.children);

  let currentSlide = 0;
  let maxSlide = slides.length - 1;

  const buttons = carousel.lastElementChild;
  const prevSlide = buttons.firstElementChild;
  const nextSlide = buttons.lastElementChild;

  prevSlide.addEventListener("click", function() {
    currentSlide === 0 ? (currentSlide = maxSlide) : (currentSlide--);
    slidesHolder.style.cssText = `transform: translateX(-${100 * currentSlide}%) translateZ(0)`;
  });
  nextSlide.addEventListener("click", function() {
    currentSlide === maxSlide ? (currentSlide = 0) : (currentSlide  );
    slidesHolder.style.cssText = `transform: translateX(-${100 * currentSlide}%) translateZ(0)`;
  });
});
:root { --aspectRatio: 80/35 } /* matches the placeholder image dimensions */

.carousel-container {
  aspect-ratio: var(--aspectRatio);
  display: grid;
  place-items: center;
  margin: auto;
  max-width: 800px;
  overflow: hidden;
  flex-grow: 1;
}

.carousel, .buttons { grid-area: 1/1/-1/-1 }

.carousel {
  display: flex;
  height: 100%;

  transition: transform 0.75s cubic-bezier(0.860, 0.000, 0.070, 1.000);
  will-change: transform:
}

.carousel .image {
  aspect-ratio: var(--aspectRatio);
  flex: 1 1 auto
}

.carousel div.image img {
  height: 100%; width: 100%;
  object-fit: cover;
}

.buttons {
  position: relative;
  width: 96%;
}

.buttons .btn-carousel {
  background-color: #fff;
  border: none;
  cursor: pointer;
  display: block;
  font-size: 18px;
  height: 40px;
  padding: 10px;
  position: absolute;
  transform: translateY( -50% );
}

.buttons .btn-carousel-prev { left: 0 }
.buttons .btn-carousel-next { right: 0 }
.buttons .btn-carousel i::before { vertical-align: baseline }
<div >
  <div >
    <div ><img loading="lazy" src="https://picsum.photos/800/350?random=1"></div>
    <div ><img loading="lazy" src="https://picsum.photos/800/350?random=2"></div>
    <div ><img loading="lazy" src="https://picsum.photos/800/350?random=3"></div>
    <div ><img loading="lazy" src="https://picsum.photos/800/350?random=4"></div>
    <div ><img loading="lazy" src="https://picsum.photos/800/350?random=5"></div>
    <div ><img loading="lazy" src="https://picsum.photos/800/350?random=6"></div>
  </div>
</div>

<hr>

<div >
  <div >
    <div ><img loading="lazy" src="https://picsum.photos/800/350?random=7"></div>
    <div ><img loading="lazy" src="https://picsum.photos/800/350?random=8"></div>
    <div ><img loading="lazy" src="https://picsum.photos/800/350?random=9"></div>
    <div ><img loading="lazy" src="https://picsum.photos/800/350?random=10"></div>
    <div ><img loading="lazy" src="https://picsum.photos/800/350?random=11"></div>
    <div ><img loading="lazy" src="https://picsum.photos/800/350?random=12"></div>
  </div>
</div>

  • Related