How to create infinite carousel in Javascript?

Viewed 102

I want to make an infinite carousel in javascript. When the elements go off-screen, I clone them and paste them at the end of the list. But after a minute there are a lot of cloned elements in the HTML layout. I decided to delete this element after cloning. But I get an instant offset of all elements. How to avoid it? Or maybe there is another algorithm on how to implement this endless carousel?

Here's the code https://codepen.io/alessandro-kex/pen/abGWNEK

window.addEventListener("load", function () {
  const slideContainer = document.querySelector(".carousel");
  const slidesWrapper = document.querySelector(".carousel-slides");
  let slides = document.querySelectorAll(".carousel-slide");
  let index = 0;
  const interval = 1500;
  let moveDistance = 0;
  const paddingRight = 50;
  let lastSlideIndex = slides.length - 1;
  let firstClone;

  const startSlide = (index) => {
    this.setInterval(() => {
      moveDistance = moveDistance + slides[index].clientWidth + paddingRight;
      slidesWrapper.style.transform = `translateX(${-moveDistance}px)`;
      slidesWrapper.style.transition = "1s";

      firstClone = slides[index].cloneNode(true);
      firstClone.id = `first-clone-${index}`;
      slidesWrapper.append(firstClone);
      /*If uncomment it - then the problem starts */
      //slides[index].remove();

      index++;
    }, interval);
  };
  startSlide(index);
});
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.carousel {
  margin: 0 auto;
  width: 100%;
  position: relative;
}

.carousel-slides {
  display: flex;
  list-style: none;
  gap: 50px;
  flex-shrink: 1;
}

.carousel-slide {
  position: relative;
  min-width: 0;
  flex-shrink: 0;
}

.carousel-slide {
  height: 100px;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
}

.carousel-item-text {
  white-space: nowrap;
}

.carousel-item-img {
  width: 100px;
  height: auto;
}

.round {
  padding: 30px 50px;
  border-radius: 120px;
}

.light-blue {
  background-color: #d8f1ff;
}

.pink {
  background-color: #ffeaf0;
}

.purpule {
  background-color: #eae9ff;
}
  <div class="carousel">
    <ul class="carousel-slides">
      <li class="carousel-slide" data-number="0">
        <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
      </li>
      <li class="carousel-slide light-blue round" data-number="1">
        <span class="carousel-item-text">11111111 1111 <br> 111111111</span>
      </li>
      <li class="carousel-slide">
        <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
      </li>
      <li class="carousel-slide pink round">
        <span class="carousel-item-text">22222 22222 <br> 222222222 2222222222 222222222</span>
      </li>
      <li class="carousel-slide">
        <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
      </li>
      <li class="carousel-slide light-blue round">
        <span class="carousel-item-text">333 333333333 <br> 333333333333</span>
      </li>
      <li class="carousel-slide">
        <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
      </li>
      <li class="carousel-slide purpule round">
        <span class="carousel-item-text">4444 444444444444<br> 44444</span>
      </li>
      <li class="carousel-slide">
        <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
      </li>
      <li class="carousel-slide  pink round">
        <span class="carousel-item-text">55555555 55555<br> 55 5555</span>
      </li>
      <li class="carousel-slide">
        <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
      </li>
      <li class="carousel-slide pink round">
        <span class="carousel-item-text">6666666 6666666666 <br> 66666 6666666 66666</span>
      </li>
      <li class="carousel-slide">
        <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
      </li>
      <li class="carousel-slide  light-blue round">
        <span class="carousel-item-text">777777 777 777<br> 77 77777</span>
      </li>
      <li class="carousel-slide">
        <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
      </li>
      <li class="carousel-slide purpule round" data-number="16">
        <span class="carousel-item-text">888 8888<br> 88888888</span>
      </li>
    </ul>
  </div>

3 Answers

You should rather create one carousel and keep all the data in an array with JavaScript. Then by default, on load, display the first element on the array. When the user clicks to view next item on carousel or the time for a certain slide expires, simply call a function to change the slide. For this, initialize a counter to keep track of slide index viewing at the moment and update it whenever the slide is changed. When the user wishes to view the next slide while they are on the last slide, set the counter to 0. Similarly, when the user wishes to view previous slide when they are on the first slide, set the counter to n-1 where n is the number of slides that you offer.

For this kind of thing you should familiarize yourself with promises

otherwise the idea is the right one,
1 - make a translateX with a transition time
2 - once the transition is finished remove the transition time
3 - put the first element at the end and reset the translateX to zero
4 - start again...

complete code :

 
(async ()=>   // async IIFE code for slider.
  {
  const
    interval       = 1500  // ms
  , paddingRight   = 50
  , slideContainer = document.querySelector('.carousel') 
  , slidesWrapper  = document.querySelector('.carousel-slides')
  , slides         = document.querySelectorAll('.carousel-slides > li')
  , delay          = ms => new Promise(r => setTimeout(r, ms))
  , movLeft = (el, mov) => new Promise(r =>
    {
    el.ontransitionend =_=>
      {
      el.ontransitionend = null
      el.style.transition = 'none';
      r()
      }
    el.style.transition = '1s';
    el.style.transform  = `translateX(${-mov}px)`;
    });

  let index = 0;

  while (true) // infinite carrousel loop
    {
    await delay( interval )
    await movLeft( slidesWrapper, slides[index].clientWidth + paddingRight  )

    slidesWrapper.appendChild( slides[index] )  // mov first slide to the end
    slidesWrapper.style.transform    = `translateX(0)` // rest translateX
    index = ++index % slides.length
    }
  })()
* {
  margin           : 0;
  padding          : 0;
  box-sizing       : border-box;
  }
.carousel {
  margin           : 0 auto;
  width            : 100%;
  position         : relative;
  }
.carousel-slides {
  display          : flex;
  list-style       : none;
  gap              : 50px;
  flex-shrink      : 1;
  }
.carousel-slide {
  position         : relative;
  min-width        : 0;
  flex-shrink      : 0;
  height           : 100px;
  display          : flex;
  align-items      : center;
  justify-content  : center;
  min-width        : 0;
  }
.carousel-item-text {
  white-space      : nowrap;
  }
.carousel-item-img {
  width            : 100px;
  height           : auto;
  }
.round {
  padding          : 30px 50px;
  border-radius    : 120px;
  }
.light-blue {
  background-color : #d8f1ff;
  }
.pink {
  background-color : #ffeaf0;
  }
.purpule {
  background-color : #eae9ff;
  }
<div class="carousel">
  <ul class="carousel-slides">
    <li class="carousel-slide" data-number="0">
      <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
    </li>
    <li class="carousel-slide light-blue round" data-number="1">
      <span class="carousel-item-text">11111111 1111 <br> 111111111</span>
    </li>
    <li class="carousel-slide">
      <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
    </li>
    <li class="carousel-slide pink round">
      <span class="carousel-item-text">22222 22222 <br> 222222222 2222222222 222222222</span>
    </li>
    <li class="carousel-slide">
      <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
    </li>
    <li class="carousel-slide light-blue round">
      <span class="carousel-item-text">333 333333333 <br> 333333333333</span>
    </li>
    <li class="carousel-slide">
      <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
    </li>
    <li class="carousel-slide purpule round">
      <span class="carousel-item-text">4444 444444444444<br> 44444</span>
    </li>
    <li class="carousel-slide">
      <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
    </li>
    <li class="carousel-slide  pink round">
      <span class="carousel-item-text">55555555 55555<br> 55 5555</span>
    </li>
    <li class="carousel-slide">
      <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
    </li>
    <li class="carousel-slide pink round">
      <span class="carousel-item-text">6666666 6666666666 <br> 66666 6666666 66666</span>
    </li>
    <li class="carousel-slide">
      <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
    </li>
    <li class="carousel-slide  light-blue round">
      <span class="carousel-item-text">777777 777 777<br> 77 77777</span>
    </li>
    <li class="carousel-slide">
      <img src="https://via.placeholder.com/150" alt="" class="carousel-item-img">
    </li>
    <li class="carousel-slide purpule round" data-number="16">
      <span class="carousel-item-text">888 8888<br> 88888888</span>
    </li>
  </ul>
</div>

new version for multiple carousels... (2022/09/20)

'use strict';

class carouselClass
  {
  static #interval     = 1500;
  static #paddingRight = 50;
  #slidesWrapper;
  #slides;
  #Li_index;
  #onLoop;

  constructor(UL_Carousel) 
    {
    this.#slidesWrapper = UL_Carousel; 
    this.#slides        = UL_Carousel.querySelectorAll(`li`);
    this.#Li_index      = 0;
    this.#onLoop        = false;
    }
  #delay( ms ) 
    {
    return new Promise(resolve => setTimeout(resolve, ms));
    }
  #movLeft( mov, indx )
    {
    return new Promise(resolve =>
      {
      this.#slidesWrapper.ontransitionend =()=>
        {
        this.#slidesWrapper.ontransitionend  = null;
        this.#slidesWrapper.style = '';
        this.#slidesWrapper.appendChild( this.#slides[this.#Li_index] ); // mov slide to the end
        this.#Li_index  = ++this.#Li_index % this.#slides.length;       // next Li element index
        resolve();
        }
      this.#slidesWrapper.style.transition = '1s';
      this.#slidesWrapper.style.transform  = `translateX(${mov}px)`;
      });
    }
  async run() 
    {
    this.#onLoop              = true;
    this.#slidesWrapper.style = '';

    while( this.#onLoop )  // infinite carrousel loop
      {
      await this.#delay( carouselClass.#interval );
      await this.#movLeft( -(this.#slides[this.#Li_index].clientWidth + carouselClass.#paddingRight) );
      } 
    }
  stop()
    {
    this.#onLoop = false;
    }
  }

const carousels = [...document.querySelectorAll('.carousel')]
  .map( cBox =>
  ({ box : cBox
   , cls : new carouselClass( cBox.querySelector('ul.carousel-slides') )
  }))

carousels.index = -1;

carousels.animateNext =_=> 
  {
  carousels.index = ++carousels.index % carousels.length;
  carousels.forEach((crl,i)=> crl.box.classList.toggle('noDisplay', carousels.index !== i) );
  carousels[carousels.index].cls.run();
  }
carousels.animateNext();  // first attempt...

bt_switch.onclick =_=>
  {
  carousels[carousels.index].cls.stop();  // stop infinite loop on current carousel
  carousels.animateNext();
  }
* {
  margin     : 0;
  padding    : 0;
  box-sizing : border-box;
  }
.carousel {
  margin   : 0 auto;
  width    : 100%;
  position : relative;
  overflow : hidden;
  }
.carousel-slides {
  display     : flex;
  list-style  : none;
  gap         : 50px;
  flex-shrink : 1;
  }
.carousel-slides img {
  height: 100px;
  }
.carousel-slides span {
  display        : inline-block;
  white-space    : nowrap;
  padding        : 30px 50px;
  border-radius  : 120px;
  }
.light-blue { background-color : #d8f1ff; }
.pink       { background-color : #ffeaf0; }
.purpule    { background-color : #eae9ff; }

.noDisplay { display : none; }
<div class="carousel">
    <ul class="carousel-slides">
      <li> <img src="https://picsum.photos/150/150?random=1"> </li>
      <li> <span class="light-blue">11111111 1111 <br> 111111111</span></li>
      <li> <img src="https://picsum.photos/150/150?random=2"> </li>
      <li> <span class="pink">2222222 2222 222 <br> 22</span></li>
      <li> <img src="https://picsum.photos/150/150?random=3"> </li>
      <li> <span class="light-blue">33 33 <br> 333 33 33 33 333 33 33 33</span></li>
      <li> <img src="https://picsum.photos/150/150?random=4"> </li>
      <li> <span class="purpule"> 444444 444 44 4<br> 44 44 44 4444  </span></li>
      <li> <img src="https://picsum.photos/150/150?random=5"> </li>
      <li> <span class="pink"> 555555 55 <br>555 555 </span></li>
      <li> <img src="https://picsum.photos/150/150?random=6"> </li>
      <li> <span class="pink"> 66666666666666666666 <br> 6 </span></li>
      <li> <img src="https://picsum.photos/150/150?random=7"> </li>
      <li> <span class="light-blue"> 77777777 777777777  777777<br> 77777777777777777 77777777777777777</span></li>
      <li> <img src="https://picsum.photos/150/150?random=8"> </li>
      <li> <span class="purpule"> 88 888 <br>  8 8 8 8 8</span></li>
    </ul>
  </div>

  <div class="carousel">
    <ul class="carousel-slides">
      <li> <img src="https://picsum.photos/150/150?random=9"> </li>
      <li> <span class="light-blue">aaa <br> 11</span></li>
      <li> <img src="https://picsum.photos/150/150?random=10"> </li>
      <li> <span class="pink">bbb bbb <br> 22</span></li>
      <li> <img src="https://picsum.photos/150/150?random=11"> </li>
      <li> <span class="light-blue">ccc <br> 3</span></li>
      <li> <img src="https://picsum.photos/150/150?random=12"> </li>
      <li> <span class="purpule"> ddd ddd <br> 4  </span></li>
      <li> <img src="https://picsum.photos/150/150?random=13"> </li>
      <li> <span class="pink"> eee eee eee <br> 5 </span></li>
      <li> <img src="https://picsum.photos/150/150?random=14"> </li>
      <li> <span class="pink"> ff ff ff <br> 6 </span></li>
      <li> <img src="https://picsum.photos/150/150?random=15"> </li>
      <li> <span class="light-blue"> g<br> g gg g gg g gg</span></li>
    </ul>
  </div>

  <br><br>
  <button id="bt_switch">___ carousel switcher ___</button>

My idea is with pure CSS. There are two containers red and blue and they overlap each-other. Here is my draft:image

I have used a=700px which is the width of the containers, and w=500px which is the width of the green wrapper. Hope this would help somebody:)

.wrapper {
        width: 500px;
        overflow-x: hidden;
        border: 2px solid black;
        position: relative;
        height: 100px;
    }
    
    .first {
        width:700px;
        height: 100px;
        background-color: red;
        position: absolute;
        left:0px;
        top:0;
        animation: firstDiv 10s linear infinite;
        
    }
    @keyframes firstDiv {
      0 {left: 0px;}
      15% {left: -200px;}
      50% {left: -700px;}
      65.9% {left: -900px;}
      66% {left: 500px;}
      99.9% {left: 0px;}
      100% {left: 0px;}
    }
    
    .second {
        width:700px;
        height: 100px;
        background-color: blue;
        position: absolute;
        left:700px;
        top:0;
        animation: secondDiv 10s linear infinite;
    }
    @keyframes secondDiv {
      0 {left: 700px;}
      15% {left: 500px;}
      50% {left: 0px;}
      65.9% {left: -200px;}
      66% {left: -200px;}
      99.9% {left: -700px;}
      100% {left: 700px;}
    }
<div class="wrapper">
        <div class="first">first container first container first container first container first container first container first container first container</div>
        <div class="second">second container second container second container second container second container second container second container</div>
     
    </div>

Related