Search code examples
javascriptarraysindexingslidercarousel

Click carousel indicator and show relevant slide


I'm building a basic "testimonial" carousel for a project. So far everything is working as expected, except making the carousel indicators clickable for each corresponding slide. The slide items should move forward or back depending on the indicator button clicked.

I believe I need to get the index of the indicator buttons and match that to the index of the slides. I'm stuck on how to get this to work in my code.

<section class="wrapper_carousel flex flex-col justify-center w-full">


            <div class="carousel-data w-full mt-8 mb-16">

                <div class="carousel-item">

                    <p class="text_caption white italic">
                        item1
                    </p>

                    <span class="caption-author white">
                        - author
                    </span>

                </div>

               <!-- carousel items 2 - 6 same as above -->

            </div>

            <div class="carousel-indicators flex items-center">
                 <!-- buttons generated w javascript -->
            </div>

            <div class="carousel-controls flex justify-end mt-auto">
                <button type="button" class="button_carousel-prev"><i class="fas fa-caret-left"></i></button>
                <button type="button" class="button_carousel-next ml-1"><i class="fas fa-caret-right"></i></button>
            </div>

        </section>
.carousel-item {
    opacity: 0;
    position: absolute;
    z-index: 500;
    transition: 500ms ease-in-out;
}

.active-slide {
    opacity: 1;
    position: relative;
    z-index: 1000;
}

.next, .prev {
    z-index: 900;
}

.next {
    animation: animate-right 500ms;
}

.prev {
    animation: animate-left 500ms;
}

@keyframes animate-right {
    0% {
        transform: translateX(0);
    }
    100% {
        transform: translateX(100%);
    }
  }

  @keyframes animate-left {
    100% {
        transform: translateX(-100%);
    }
    0% {
        transform: translateX(0);
    }
  }

.carousel-indicators {
    margin: 0 auto;
}

.carousel-indicators button {
    width: 50px;
    height: 8px;
    background: var(--mid-grey);
    margin-left: 8px;
}

.carousel-indicators button:first-of-type {
    margin-left: 0;
}

.carousel-controls button {
    background: var(--yellow);
    padding: 24px;
    font-size: var(--text_md);
    width: 60px;
    max-width: 100%;
    height: 60px;
    max-height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
}

.carousel-controls button .fas {
    line-height: 1em;
    font-size: 1.25em;
    pointer-events: none;
}

button.current-indicator {
    background: var(--yellow);
}

let position = 0;
const slides = document.querySelectorAll('.carousel-item');
const lastSlide = document.querySelector('.carousel-item:last-of-type');
const totalSlides = slides.length;
const indicatorContainer = document.querySelector('.carousel-indicators');
const indicatorButtons = [];

function initialSlide() {

    if (position == 0) {
        slides[position].classList.add('active-slide');
    }

}

function setIndicators() {

    if (indicatorContainer !== null) {
        for (let i = 0; i < totalSlides; i++) {
            const button = document.createElement('button');
            button.setAttribute('type', 'button');
            indicatorContainer.appendChild(button);
            indicatorButtons.push(button);
        }

        indicatorButtons[0].classList.add('current-indicator');

    }

}

window.onload = function (event) {
    initialSlide();
    setIndicators();
  };

  function updateIndicators() {

    indicatorButtons.forEach( function(element, index){

        if (index === position) {
            element.classList.add('current-indicator');
        } else {
            element.classList.remove('current-indicator');
        }

    });
}

  function updatePosition() {

    for (let slide of slides) {
        slide.classList.remove('active-slide');
        slide.classList.remove('next');
        slide.classList.remove('prev');
    }

    slides[position].classList.add('active-slide');

    updateIndicators();

  }

  function animateRight() {

      if (position === 0) {
        lastSlide.classList.add('next');
      } else {
        slides[position - 1].classList.add('next');
      }
 
  }

  function animateLeft() {

      if (position === totalSlides - 1) {
          slides[0].classList.add('prev');
      } else {
        slides[position + 1].classList.add('prev');
      }
    
  }

  function moveForward() {

      if (position === totalSlides - 1) {
          position = 0;
      } else {
            position++;
      }

      updatePosition();
      animateRight();
  }

  function moveBack() {

    if (position === 0) {
        position = totalSlides - 1;
    } else {
          position--;
    }

    updatePosition();
    animateLeft();
}

document.addEventListener('click', function (event) {
    if (event.target.matches('.button_carousel-next')) {
        moveForward();
    }

    if (event.target.matches('.button_carousel-prev')) {
        moveBack();
    }

    if (event.target.matches('.carousel-indicators button')) {
        
    }

}, false);


Solution

  • You can do it like that:

    add one line here:

    function setIndicators() {
    ...
    button.setAttribute('type', 'button');
    button.setAttribute('btindex', i); // <--
    indicatorContainer.appendChild(button);
    

    then in the event listener last statement will be:

    if (event.target.matches('.carousel-indicators button')) {
        moveSelected(parseInt(event.target.getAttribute('btindex')));
    }
    

    and new method will be:

    function moveSelected(selectedId) {
    
        var prevPos = position;
        position = selectedId;
    
        updatePosition();
    
        if (position > prevPos) {
            animateRight();
        }else if (position < prevPos) {
            animateLeft();
        }
    }