Add Prev Next Controls to Carousel

Viewed 406

After toying about with timers and intervals I have come to a solution that works to my satisfaction.

See relevant jsFiddle or code below:

HTML:

<div id="foo">irrelevant content</div>

javascript( with jQuery):

var post_array = [ "abc", "123", "xyz" ];
var class_array = [ "red", "blue", "green" ];

var interval = 2000;
var i = 0;
var max = post_array.length;
var id ="#foo";

$(id).html(post_array[0]);
$(id).removeClass().addClass(class_array[0]);

setInterval( function(){
    ++i;
    $(id).fadeOut("slow", function() {
    $(id).html(post_array[i%max]).fadeIn("slow");
    $(id).removeClass().addClass(class_array[i%max]);
  });
}, interval);

Now I wonder what the best way to add two side arrows that allow me to go back and fort would be.

should I have written the relevant code in a named function so I can call it and pass an index parameter when the button is pressed? ( how do i act on the same index variable in that case? ) What's the best practice for button overlays?

Help! Thanks in advance

1 Answers
  • Carousels should be modular, reusable and extendable. Don't copy paste JS code when in need to add another Carousel into your DOM.
  • In order to create PREV / NEXT buttons you'll also need a method to stop your interval: stop
  • When you hover over your Carousel, you'll need to pause the autoplay to prevent a really bad User Experience (UX)
  • Don't animate using jQuery. Animate by simply assigning an is-active class to the current index slide, and use CSS to do whatever you want with that class.
  • Use a variable index (start with 0) to keep track of the current slide index
  • You Might Not Need jQuery

Aim to create a class instance using the sugary class or the proper prototype syntax - that can be used like:

const myCarousel = new Carousel({
  target: "#carousel-one",
  slides: [
    {
      title: "This is slide one",
      image: "images/one.jpg"
    },
    {
      title: "This is slide two! Yey.",
      image: "images/two.jpg"
    }
  ]
});

So basically, you'll need a constructor that has those methods:

Method Description
anim() Fix index if exceeds slides or is negative and animate to new index
prev() Decrement index and trigger anim()
next() Increment index and trigger anim()
stop() Clear loop interval (On mouseenter)
play() Start loop (Triggers next() every pause milliseconds)

class Carousel {
  constructor(options) {

    Object.assign(this, {
      slides: [],
      index: 0,
      pause: 4000, // Pause between slides
      EL: document.querySelector(options.target || "#Carousel"),
      autoplay: true,
    }, options);

    this.total = this.slides.length;
    this.EL_area = this.EL.querySelector(".Carousel-area");
    this.EL_prev = this.EL.querySelector(".Carousel-prev");
    this.EL_next = this.EL.querySelector(".Carousel-next");

    const NewEL = (tag, prop) => Object.assign(document.createElement(tag), prop);

    // Preload images
    this.ELs_items = this.slides.reduce((DF, item) => {
      const EL_slide = NewEL("div", {
        className: "Carousel-slide"
      });
      const EL_image = NewEL("img", {
        className: "Carousel-image",
        src: item.image,
        alt: item.title
      });
      const EL_content = NewEL("div", {
        className: "Carousel-title",
        textContent: item.title
      });
      EL_slide.append(EL_image, EL_content);
      DF.push(EL_slide);
      return DF;
    }, []);
    this.EL_area.append(...this.ELs_items);

    // Events
    this.EL_prev.addEventListener("click", () => this.prev());
    this.EL_next.addEventListener("click", () => this.next());
    this.EL.addEventListener("mouseenter", () => this.stop());
    this.EL.addEventListener("mouseleave", () => this.play());

    // Init
    this.anim();
    this.play();
  }

  // Methods:

  anim() {
    this.index = this.index < 0 ? this.total - 1 : this.index >= this.total ? 0 : this.index;
    this.ELs_items.forEach((EL, i) => EL.classList.toggle("is-active", i === this.index));
  }

  prev() {
    this.index -= 1;
    this.anim();
  }

  next() {
    this.index += 1;
    this.anim();
  }

  stop() {
    clearInterval(this.itv);
  }

  play() {
    if (this.autoplay) this.itv = setInterval(() => this.next(), this.pause);
  }
}


// Use like:
new Carousel({
  target: "#carousel-one",
  slides: [{
      title: "We're part of nature",
      image: "https://picsum.photos/id/10/400/300"
    },
    {
      title: "Remember to read and learn",
      image: "https://picsum.photos/id/24/400/300"
    },
    {
      title: "Up for a coffee?",
      image: "https://picsum.photos/id/30/400/300"
    },
  ]
});
/* CAROUSEL */

.Carousel {
  position: relative;
  height: 300px;
  max-height: 100vh;
}

.Carousel-slide {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  transition: opacity 0.5s; /* DESIRED SLIDE TRANSITIONS */
  opacity: 0;               /* INACTIVE SLIDE*/
}

.Carousel-slide.is-active { /* ACTIVE SLIDE! */
  opacity: 1;
  z-index: 1;
}

.Carousel-prev,
.Carousel-next {
  position: absolute;
  z-index: 2;
  top: 50%;
  transform: translateY(-50%);
  user-select: none; /* Prevent highlight */
}

.Carousel-prev {
  left: 1em;
}
.Carousel-next{
  right: 1em;
}

.Carousel-image {
  position: absolute;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.Carousel-title {
  position: absolute;
  width: 100%;
  height: 100%;
  color: #fff;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 3em;
}
<div class="Carousel" id="carousel-one">
  <div class="Carousel-area"></div>
  <button class="Carousel-prev" type="button" aria-label="Previous slide">&larr;</button>
  <button class="Carousel-next" type="button" aria-label="Next slide">&rarr;</button>
  <div class="Carousel-desc"></div>
</div>

With the above code you can have an unlimited number of carousels on a single page given every one has a different target ID.


PS: Alternatively, if your code keeps track of the direction for the prev / next, the logic to increment/decrement/loopback the current index can be also written as (pseudocode ahead!):

C = (is_next ? ++C : --C) < 0 ? T-1 : C%T;

where C is the current index, T is the total number of slides, and is_next is a boolean that is true when the direction is Next.

Related