JSFiddle - React, Tailwind, and code Playground

by rainyjune

HTML

<div class="parentEl">
  <div id="container">
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item">5</div>
    <div class="item">6</div>
    <div class="item">7</div>
  </div>

  <div class="controls">
    <button class="toggle" data-toggle="prev">Prev</button>
    <button class="toggle" data-toggle="next">Next</button>
  </div>

  <div class="dots" style="text-align: center"></div>

</div>

CSS

#container {
scroll-snap-type: x mandatory;
display: flex;
overflow-x: scroll;
width: 100%;
}

.item {
border: 1px solid red;
min-width: 33%;
height: auto;
scroll-snap-align: start;
}

#container {
scroll-behavior: smooth;
}

/* Hide the horizontal scroll bar */

/* Hide scrollbar for Chrome, Safari and Opera */
#container::-webkit-scrollbar {
display: none;
}

/* Hide scrollbar for IE, Edge and Firefox */
#container {
-ms-overflow-style: none;  /* IE and Edge */
scrollbar-width: none;  /* Firefox */
}

/** Controls **/
.controls {
  padding: 1em;
  text-align: center;
}

.controls button {
  background: #aaa;
  border: 0;
  border-radius: 0.25em;
  color: #eee;
  padding: 0.5em 1em;
}

.controls button:hover,
  .controls button:focus {
      background: orange;
  }

  /* The dots/bullets/indicators */
  .dot {
      cursor: pointer;
      height: 13px;
      width: 13px;
      margin: 0 2px;
      background-color: #bbb;
      border-radius: 50%;
      display: inline-block;
      transition: background-color 0.6s ease;
  }

  .active, .dot:hover {
      background-color: #717171;
  }

JavaScript

window.onload = function() {
  const parentEl = document.querySelector('.parentEl');
  const container = document.querySelector('#container');
  const dotsWrapperEl = document.querySelector('.dots');
  const items = container.querySelectorAll('.item');
  const nextBtn = parentEl.querySelector('.controls [data-toggle="next"]');
  const prevBtn = parentEl.querySelector('.controls [data-toggle="prev"]');
  let currentIndex = 0;

  nextBtn.addEventListener('click', function(e) {
    currentIndex = (currentIndex + 1) % items.length;
    updateUI();
  });

  prevBtn.addEventListener('click', function(e) {
    currentIndex--;
    if (currentIndex < 0) currentIndex = items.length - 1;
    updateUI();
  });

  dotsWrapperEl.addEventListener('click', function(e) {
    const target = e.target;
    const dotList = dotsWrapperEl.querySelectorAll('.dot');
    if (target.classList.contains('dot')) {
      const index = getDotIndex(target, dotList);
      currentIndex = index;
      updateUI();
    }
  });

  function getDotIndex(target, dotList) {
    for (let i = 0; i < dotList.length; i++) {
      if (dotList[i] === target) return i;
    }
    return -1;
  }

  function updateUI() {
    dotsWrapperEl.querySelector('.active')?.classList.remove('active');
    dotsWrapperEl.querySelectorAll('.dot')[currentIndex].classList.add('active');

    const ar = getOrder(currentIndex);
    for (let i = 0; i < items.length; i++) {
      items[i].style.order = ar[i];
    }

    items[currentIndex].scrollIntoView({block: "start", inline: "start"});
  }

  function getOrder(rotateCount = 0) {
    const arr = Array.from({length: items.length}, (x, i) => i + 1);
    arr.push(arr.shift());
    if (rotateCount == 0) return arr;
    const ar = arr.splice(items.length - rotateCount, rotateCount);
    arr.splice(0, 0, ...ar);
    return arr;
  }

  getOrder();
  dotsWrapperEl.innerHTML = '<span class="dot"></span>'.repeat(items.length);

  updateUI();
};