Changing my carousel info on click

Hi

I have an automated carousel that shows different testimonials automatically. I have some indicators below that change color with the corresponding information. What I would like to do is click on the indicator and show the relevant information without stopping/disrupting the automated carousel. Does anyone know how this can be done? I’ve tried using the change image function onclick within the HTML, but this seems to screw up the carousel completely.

This is my JS

var slideIndex = 0;
carousel();

function carousel() {
    
    var x = document.querySelectorAll(".profiles article");
    var y = document.querySelectorAll(".indicators div");

    
    for (i = 0; i < x.length; i++) {
      y[i].classList.remove("active");
      x[i].classList.remove("profile-active"); 
    }
    slideIndex++;
    if (slideIndex > x.length) {slideIndex = 1}
    x[slideIndex-1].classList.add("profile-active");
    y[slideIndex-1].classList.add("active");
    setTimeout(carousel, 4000);
}

function changeImage(){
    slideIndex = 0;
    carousel();
};

HTML

<section id="testimonials" class="style bg-off-white pd-bottom">
            <h2 class="title pd-top txt-white">Testimonials</h2>
            <section class="profiles">
                <article class="profile-active">
                    <img src="public/images/profile-1.jpg" alt="Profile pic of Jane Doe">
                    <p class="text-style">
                        Lorem ipsum dolor sit amet consectetur adipisicing elit. 
                        Quasi, repellendus in tempore expedita accusantium assumenda 
                        minus quaerat perferendis placeat veniam nostrum unde odio incidunt nisi?</p>
                    <p class="text-style"><span>Jane Doe</span>, CEO, company.</p>
                </article>
                <article>
                    <img src="public/images/profile-2.jpg" alt="Profile pic of John Doe">
                    <p class="text-style">
                        Lorem ipsum dolor sit amet consectetur adipisicing elit. 
                        Quasi,  expedita accusantium assumenda veniam nostrum unde odio incidunt 
                        nisi?</p>
                    <p class="text-style"><span>John Doe</span>, CEO, company.</p>
                </article>
                <article>
                    <img src="public/images/profile-3.jpg" alt="Profile pic of Jack Doe">
                    <p class="text-style">
                        Lorem ipsum dolor sit amet consectetur adipisicing elit. 
                        Blanditiis reprehenderit totam nemo quo. Itaque, repudiandae. Assumenda sit vitae veniam? Numquam iste culpa maiores. 
                        Excepturi, alias quae et perferendis voluptatem inventore.</p>
                    <p class="text-style"><span>Jack Doe</span>, CEO, company.</p>
                </article>
            </section>
                <section class="indicators">
                    <div></div>
                    <div></div>
                    <div></div>
                </section>
        </section>

Website in Question

https://kubix90.github.io/CSS_website_clone/

Ok, I’m going to get you started, but let you finish meeting your requirements.

But first, here’s a little trick for looping in a circular fashion over a range. This is very useful for carousels. For this we’ll use the % operator.

Suppose we want to loop between 0-2, the indices of our array, but loop back to 0 if we go over 2. This is equal to

if (x < 3) loop back to 0.

So 3 will be our “turning point”.

0 % 3 // 0
1 % 3 // 1
2 % 3 // 2
3 % 3 // 0 ?
4 % 3 // 1
5 % 3 // 2 
6 % 3 // 0

This trick works because the % operator will return the whole number if there is no remainder, but return the remainder when there is one.

You don’t have to use this trick. But I used it to remove tracking the value of slideIndex in the code below.

Now on to the code. I refactored it to get you started. Here’s what I did

  1. move selectors to the outer scope
    • you only need to access them once
    • but carousel function is looping.
    • therefore you’re reinitializing the variables each time, even though the values never change.
  2. change setTimeout to setInterval
    • setTimeout only runs the code once
      • this is why you had to call it manually on each loop.
    • setInterval will loop until cancelled with clearInterval

Here’s a nice article explaining the difference https://javascript.info/settimeout-setinterval

var slideIndex = 0;
var x = document.querySelectorAll(".profiles article");
var y = document.querySelectorAll(".indicators div");
var timeOut = setInterval(carousel, 4000);

function carousel() {

  for (i = 0; i < x.length; i++) {
    y[i].classList.remove("active");
    x[i].classList.remove("profile-active");
  }

  // no need to track slideIndex - 1
  x[slideIndex].classList.add("profile-active");
  y[slideIndex].classList.add("active");
  
  // increase slideIndex by one, looping to start if at end
  slideIndex = (slideIndex += 1) % x.length

}

Now in your onClick handler you should be able to set the index to the appropriate value, and it will keep looping until you clear it with clearInterval

function changeImage() {
  slideIndex = 0;
}

Try that and tell me how it works for you. If there’s something I haven’t made clear enough, just tell me and we’ll sort it out.

1 Like

So what I was doing was using the changeImage function above within the onclick action within the HTML like so.

                <section class="indicators">
                    <div onclick="changeImage(1)"></div>
                    <div></div>
                    <div></div>
                </section>

My thought process being that If i click and change the value of the slideIndex, then the carousel will change to the relevant testimonial, but it just results in the carousel jumping about uncontrollably.

Cheers dude, this has worked in the main. Had to make a few adjustments to get it working. The only thing I’m confused about is why I have to set the slideIndex to 1. When I set it to 0, it stays on the first testimonial for 8 seconds then works as normal, which I don’t really understand. Setting it to 1 has fixed this though. Code is below for info.

                <section class="indicators">
                    <div class="active" onclick="changeInfo(0)"></div>
                    <div onclick="changeInfo(1)"></div>
                    <div onclick="changeInfo(2)"></div>
                </section>

var slideIndex = 1;
var x = document.querySelectorAll(".profiles article");
var y = document.querySelectorAll(".indicators div");
var timeOut = setInterval(carousel, 4000);

function carousel() {

  for (i = 0; i < x.length; i++) {
    y[i].classList.remove("active");
    x[i].classList.remove("profile-active");
  }

  x[slideIndex].classList.add("profile-active");
  y[slideIndex].classList.add("active");

  slideIndex = (slideIndex += 1) % x.length

}

function changeInfo(n){ 
    slideIndex = n;
    carousel();
};

This is because you already have the active class on the first testimonial. So when the carousel function is called 4sec later, it adds the class back to index 0. You were right to start it at 1.

Also, you don’t have to add multiple onclick handlers. It’s fine for a simple website like this, but I encourage you to look into event bubbling. Once you have to add many onclick handlers performance and maintenance decline.

Here’s a great website that explains bubbling – as well as many other things

Then you can attach a data-* attribute to the element

<section class="indicators">
      <div class="active" data-idx='0'></div>
      <div data-idx='1'></div>
      <div data-idx='2'></div>
    </section>

and in your js file grab the value from there

var indicatorSection = document.querySelectorAll("section .indicators")[0]
// add event listener to capture bubbled events, onclick won't work
indicatorSection.addEventListener('click', changeInfo)

// then 
function changeInfo(n){ 
    slideIndex = n.target.dataset.idx;
    carousel();
};

Great job on the site btw. Looks well done and responsive.

Thanks appreciate it. Thanks for the links, some really interesting stuff that I had no clue about, will try and fit some of this into my future projects.