0

I need to make a carousel/slideshow in plain JavaScript mixed with CSS that slides through the images one by one loop seamlessly.

I can't seem to get any code working. I've tried several approaches but can't. It's got to be with z-index, and not making use of flex.

This is my third attempt at coding this. Can't seem to get the logic. It has to have navigation buttons to switch between images. Can someone help me out?

  const getHeader = document.querySelector('.wp-custom-header');
  const getImages = document.querySelectorAll('.wp-custom-header img');
  const computeImages = function () {
    getImages.forEach((img, index) => {
      if (index > 0) img.classList.add('out');
    });
  };
  computeImages();
  let counter = 0;
  let reinit = false;
  // getHeader.classList.add('transformSlide');
  const slideShowTimer = setInterval(() => {
    if (counter > 0 && counter < getImages.length - 1) {
      getImages[counter + 1].classList.add('transform-slide');
      getImages[counter + 1].classList.add('onqueue-current');
      getImages[counter + 1].classList.remove('out');
    } else if (counter === 0 && reinit === false) {
      getImages[counter + 1].classList.add('transform-slide');
      getImages[counter + 1].classList.add('onqueue-current');
      getImages[counter + 1].classList.remove('out');
    } else if (counter === 0 && reinit === true) {
      getImages[counter].classList.add('transform-slide');
      getImages[counter].classList.add('onqueue-current');
      getImages[counter].classList.remove('out');
      getImages[getImages.length - 1].classList.add('out');
      getImages[getImages.length - 1].classList.remove('transform-slide');
      getImages[getImages.length - 1].classList.remove('onqueue-current');
    }
    counter++;
  }, 2000);

  getHeader.addEventListener('transitionend', () => {
    if (counter >= 1) {
      if (!reinit) {
        getImages[counter - 1].classList.remove('transform-slide');
        getImages[counter - 1].classList.remove('onqueue-current');
        getImages[counter - 1].classList.add('out');
      } else {
        getImages[counter].classList.remove('transform-slide');
        getImages[counter].classList.remove('onqueue-current');
        getImages[counter].classList.add('out');
      }
    }
    if (counter >= getImages.length - 1) {
      console.log(counter);
      counter = 0;
      reinit = true;
    }
  });

This is the HTML

  <div id="wp-custom-header" class="wp-custom-header">    
    <img alt="" src="./image01.svg" />
    <img alt="" src="./image02.svg" />
    <img alt="" src="./image03.svg" />
    <img alt="" src="./image04.svg" />
    <img alt="" src="./image05.svg" />
    <img alt="" src="./image06.svg" />
  </div>

The CSS

.wp-custom-header {
  position: relative;
  display: block;
  width: 100%;
  height: var(--header-size);
}

.wp-custom-header img {
  position: absolute;
  display: block;
  min-width: 100%;
  -o-object-fit: cover;
  object-fit: cover;
  transition: var(--slide-transform) ease-in-out;
}
.wp-custom-header img.out {
  /* left: -450px; */
  z-index: 0;
  transform: translateX(100%);
}
.wp-custom-header img.onqueue-next {
  z-index: 0;
  left: 0px;
}
.wp-custom-header img.onqueue-current {
  z-index: 1;
  transform: translateX(0px);
}
.transform-slide {
  transition: var(--slide-transform) ease-in-out;
}
  • Hey in case you're looking for a simpler implementation, check this out - https://codepen.io/lapstjup/pen/RwoRWVe I created this a week ago and might be helpful. Also for more minimal implementation, check this out - https://stackoverflow.com/questions/66102228/what-could-be-the-bare-minimum-steps-to-animate-the-following-carousel-implement – Lakshya Thakur Feb 13 '21 at 19:51
  • *It's got to be with z-index, and not making use of flex*, could you explain why this is a restriction or what you mean? They both do very different things. – Emiel Zuurbier Feb 13 '21 at 19:57
  • @EmielZuurbier It's a problem with my english, my apologies, it's more of a slideshow that autoplays with navigation buttons. It was requested to me to do it that way with zindex. The reason is that it consumes less resources, as I was told. Can't do it in another way. –  Feb 13 '21 at 20:00
  • @LakshyaThakur thanks for the info. But it's got to automatically and seamlessly change to the first image after the last one. forward slide, not translanteX back to the beginning. –  Feb 13 '21 at 20:02

1 Answers1

0

I took the liberty to tinker with your code. I've reworked your code into smaller functions and added a looping mechanic. Now you have buttons that will loop infinitely no matter how many slides there are in your carousel.

I've added a previous and a next button. Hovering over the images will stop the autoslide functionality from running so that you can control going to the next and previous slide. Whenever you stop hovering the carousel continues.

Hope that this is what you were looking for.

const header = document.querySelector('.wp-custom-header');
const images = document.querySelectorAll('.wp-custom-header img');
const buttons = document.querySelectorAll('.wp-custom-header button');

let activeSlideIndex = 0;
let interval = null;

const updateCarousel = () => {
  images.forEach((image, index) => {
    if (index === activeSlideIndex) {
      image.classList.add('active');
    } else if (image.classList.contains('active')) {
      image.classList.remove('active');
    }
  });
};

const nextSlide = () => {
  if (activeSlideIndex + 1 < images.length) {
    activeSlideIndex++;
  } else {
    activeSlideIndex = 0;
  }
};

const prevSlide = () => {
  if (activeSlideIndex - 1 >= 0) {
    activeSlideIndex--;
  } else {
    activeSlideIndex = images.length - 1;
  }
};

const startInterval = () => setInterval(() => {
  nextSlide();
  updateCarousel();
}, 2000);

const stopInterval = () => {
  clearInterval(interval);
  interval = null;
};

interval = startInterval();

const controls = {
  'prev': prevSlide,
  'next': nextSlide
};

header.addEventListener('mouseenter', () => {
  if (interval !== null) {
    stopInterval();
  }
});

header.addEventListener('mouseleave', () => {
  interval = startInterval();
});

buttons.forEach(button => {
  button.addEventListener('click', event => {
    const value = event.target.value;
    const action = controls[value];
    action();
    updateCarousel();
  });
});
.wp-custom-header {
  position: relative;
}

.wp-custom-header-images {
  display: block;
  width: 100%;
  height: 250px;
}

.wp-custom-header-images img {
  position: absolute;
  display: block;
  width: 100%;
  height: 100%;
  -o-object-fit: cover;
  object-fit: cover;
  opacity: 0;
  will-change: opacity;
  transition: opacity 250ms ease-in-out;
  z-index: 0;
}

.wp-custom-header-images img.active {
  z-index: 1;
  opacity: 1;
}

.wp-custom-header-button {
  position: absolute;
  top: 50%;
  border: 1px solid #d0d0d0;
  background-color: #f0f0f0;
  border-radius: 50%;
  width: 50px;
  height: 50px;
  cursor: pointer;
  transform: translate(0, -50%);
  z-index: 2;
}

.wp-custom-header-button[value="prev"] {
  left: 15px;
}

.wp-custom-header-button[value="next"] {
  right: 15px;
}
<div id="wp-custom-header" class="wp-custom-header">
  <button class="wp-custom-header-button" value="prev">Prev</button>
  <div class="wp-custom-header-images">
    <img alt="" src="https://picsum.photos/seed/a/640/360" class="active" />
    <img alt="" src="https://picsum.photos/seed/b/640/360" />
    <img alt="" src="https://picsum.photos/seed/c/640/360" />
    <img alt="" src="https://picsum.photos/seed/d/640/360" />
    <img alt="" src="https://picsum.photos/seed/e/640/360" />
    <img alt="" src="https://picsum.photos/seed/f/640/360" />
  </div>
  <button class="wp-custom-header-button" value="next">Next</button>
</div>
Emiel Zuurbier
  • 19,095
  • 3
  • 17
  • 32
  • The code is great. Very effective. But I was looking for a slide effect, not an opacity effect. :\ –  Feb 13 '21 at 21:19
  • I'll accept the answer if the person who requested this from me accepts it –  Feb 13 '21 at 21:53
  • I assumed that this what you wanted because you specifically said you needed a solution with `z-index` (meaning overlapping structure) and not with `display: flex` (meaning row or column). Otherwise I'd just suggest using an existing carousel library like [swiper](https://swiperjs.com/swiper-api) or [slick slider](https://kenwheeler.github.io/slick/) which have the features you request. – Emiel Zuurbier Feb 13 '21 at 21:57