Odo Carousel

Performant, awesome carousels.

Support

IE9+ with polyfills.

To support IE<=11, you will need to add classList and Element#closest polyfills.

Dependencies

Odo Device, Odo Helpers, Odo Draggable, Tiny Emitter.

Odo Pointer, Draggable, and Carousel also expect Object.assign from ES6 to be available.

Styles

This component has required css - css/odo-carousel.css. The theme (css/odo-carousel-theme.css) should also be included and customized to your design.

Regular

By default, carousels are draggable, have overflow, are horizontal, are not looped, are not slideshows, are not “jumped”, and do not have pagination. You can add slides, reset the carousel so it purges its cache of elements and finds the new ones, set the selected index, go previous or next slides, start a slideshow, pause a slideshow, and use the other public methods.

Markup

<h2 id="regular-title">Regular</h2>
<div id="regular" class="odo-carousel" aria-labelledby="regular-title">
  <div class="odo-carousel__wrapper">
    <ul class="odo-carousel__element">
      <li class="odo-carousel__slide">1</li>
      <li class="odo-carousel__slide">2</li>
      <li class="odo-carousel__slide">3</li>
      <li class="odo-carousel__slide">4</li>
    </ul>
  </div>
</div>

Setup

var regular = new OdoCarousel(document.getElementById('regular'));

Methods

You can also enable/disable the carousel by setting instance.isEnabled.

// Some useful public methods.
goToPreviousSlide();
goToNextSlide();
setSelectedIndex(zeroBasedIndex);
setDraggable(shouldBeDraggable);
isFirstSlide();
isLastSlide();
getSelectedIndex();
getCarouselElement();
getSlides();
getSlide(zeroBasedIndex);
addSlide(slideHtml);
reset();
startSlideshow();
pauseSlideshow();

Looped with images

Add the isLooped option to make the carousel cycle infinitely. It takes slides from the start and appends them to the end when going forwards, and in end to start going backwards.

Setup

var looped = new OdoCarousel(document.getElementById('looped'), {
  isLooped: true
});

Add a slide

var slideHtml = '<div class="odo-carousel__slide">5</div>';
addSlide(slideHtml);

Jumped and Looped with pagination

By default, the carousel does not add pagination. To add pagination, add a pagination option to the carousel. This demo also shows how you can change the easing of the animation with easing: 'ease'.

Setup

var jumped = new OdoCarousel(document.getElementById('jumped'), {
  isLooped: true,
  isJumped: true,
  pagination: true,
  easing: 'ease'
});

Vertical and Looped

Carousels can be vertical too with isVertical option. This demo changes the duration of the animation with animationSpeed: 500. The paddles and pagination are customized with getNavPaddleHtml and getPaginationHtml. You have access to the carousel's templating method through OdoCarousel.template.

Setup

var vertical = new OdoCarousel(document.getElementById('vertical'), {
  isVertical: true,
  isLooped: true,
  animationSpeed: 500,
  pagination: true,
  template: {
    paddleNextInner: '<svg viewBox="0 0 24 24"><path d="M7.4 8.3L6 9.7l6 6 6-6-1.4-1.4-4.6 4.6z"></path></svg>',
    paddlePrevInner: '<svg viewBox="0 0 24 24"><path d="M16.6 15.7l1.4-1.4-6-6-6 6 1.4 1.4 4.6-4.6 4.6 4.6z"/></svg>'
  },
  getNavPaddleHtml: function () {
    return '';
  },
  getPaginationHtml: function (instance) {
    var totalSlides = instance.getSlides().length;

    var previousPaddle = OdoCarousel.template(instance.options.template.paddlePrev, {
      paddleInner: instance.options.template.paddlePrevInner,
    });
    var nextPaddle = OdoCarousel.template(instance.options.template.paddleNext, {
      paddleInner: instance.options.template.paddleNextInner,
    });

    var dotsHtml = '';
    for (var i = 0; i < totalSlides; i++) {
      dotsHtml += OdoCarousel.template(instance.options.template.paginationDot, {
        index: i,
        index1: i + 1,
        slideId: instance.getSlide(i).id,
      });
    }

    var open = '<nav role="tablist" class="' + OdoCarousel.Classes.PAGINATION +
      ' ' + OdoCarousel.Classes.PADDLES + '">';
    var close = '</nav>';

    return open + previousPaddle + dotsHtml + nextPaddle + close;
  }
});

Looped Multiple Neighbors

A slide neighbor is the slide which is beside another. You can change the number of neighbors the active slide has with the neighborCount option. This one has 4 neighbors.

Markup

<div id="neighbors" class="odo-carousel">
  <div class="odo-carousel__wrapper">
    <div class="odo-carousel__element">
      <div class="odo-carousel__slide" data-color="1">1</div>
      <div class="odo-carousel__slide" data-color="2">2</div>
      <div class="odo-carousel__slide" data-color="3">3</div>
      <div class="odo-carousel__slide" data-color="4">4</div>
      <div class="odo-carousel__slide" data-color="5">5</div>
      <div class="odo-carousel__slide" data-color="6">6</div>
      <div class="odo-carousel__slide" data-color="7">7</div>
      <div class="odo-carousel__slide" data-color="8">8</div>
      <div class="odo-carousel__slide" data-color="9">9</div>
      <div class="odo-carousel__slide" data-color="10">10</div>
    </div>
  </div>
</div>

Setup

var neighbors = new OdoCarousel(document.getElementById('neighbors'), {
  isLooped: true,
  neighborCount: 4
});

Centered Slides

The isCentered option will position the slides in the center of the carousel wrapper element. This works well when your slides are smaller than the carousel wrapper.

Setup

var centered = new OdoCarousel(document.getElementById('centered-demo'), {
  isCentered: true,
});

Looped carousels containing only 2 slides need special rendering to allow bidirectional navigation. Simply setting isLooped: true will properly enable this functionality.

Markup

<div id="bidirectional" class="odo-carousel">
  <div class="odo-carousel__wrapper">
    <div class="odo-carousel__element">
      <div class="odo-carousel__slide" data-color="1">1</div>
      <div class="odo-carousel__slide" data-color="2">2</div>
    </div>
  </div>
</div>

Setup

var bidirectional = new OdoCarousel(document.getElementById('bidirectional'), {
  isLooped: true,
  pagination: true
});

Slideshow

To autplay the carousel, you must start it with startSlideshow(). If the carousel is not looped, the slideshow will stop at the end.

Setup

var slideshow = new OdoCarousel(document.getElementById('slideshow'), {
  slideshowSpeed: 1500,
  animationSpeed: 600
});

Playing and Pausing the Slideshow

startSlideshow();
pauseSlideshow();

Slide Children with start index

Add the special class name odo-carousel__slide-child and when the carousel gets to the last slide, it will use the widths of the slide children so that the whole last slide does not move over completely.

Slide Markup

<div class="odo-carousel__slide">
  <div class="odo-carousel__slide-child third">1A</div>
  <div class="odo-carousel__slide-child third">1B</div>
  <div class="odo-carousel__slide-child third">1C</div>
</div>

<div class="odo-carousel__slide">
  <div class="odo-carousel__slide-child third">2A</div>
  <div class="odo-carousel__slide-child third">2B</div>
  <div class="odo-carousel__slide-child third">2C</div>
</div>

<div class="odo-carousel__slide">
  <div class="odo-carousel__slide-child third">3A</div>
</div>

CSS

.slide-children-demo .odo-carousel__slide-child.third {
  float: left;
  width: 33.333%;
  height: 100%;
}

Setup

var slideChildren = new OdoCarousel(document.getElementById('slide-children'), {
  startIndex: 1,
  template: {
    paddleNextInner: '<div style="width:100%;height:100%;background-color:rgba(0, 0, 0, 0.5);border-radius:50%;"></div>',
    paddlePrevInner: '<div style="width:100%;height:100%;background-color:rgba(0, 0, 0, 0.5);border-radius:50%;"></div>'
  }
});

Vertical Slide Children

Slide Markup

<div class="odo-carousel__slide">
  <div class="odo-carousel__slide-child third">1A</div>
  <div class="odo-carousel__slide-child third">1B</div>
  <div class="odo-carousel__slide-child third">1C</div>
</div>

<div class="odo-carousel__slide">
  <div class="odo-carousel__slide-child third">2A</div>
  <div class="odo-carousel__slide-child third">2B</div>
  <div class="odo-carousel__slide-child third">2C</div>
</div>

<div class="odo-carousel__slide">
  <div class="odo-carousel__slide-child third">3A</div>
  <div class="odo-carousel__slide-child third">3B</div>
</div>

CSS

.vertical-slide-children-demo .odo-carousel__slide-child.third {
  width: 100%;
  height: 33.333%;
}

These behave like slideshows. They have touch/pointer/mouse events bound to them and will change slides when it interprets a horizontal swipe. Devices without CSS transitions will not see a fade animation (IE9).

Slide Markup

Is the same as regular carousels, except the main carousel element has a odo-carousel--fade class on it.

<div id="darth-fader" class="odo-carousel odo-carousel--fade">
  <div class="odo-carousel__wrapper">
    <div class="odo-carousel__element">
      <div class="odo-carousel__slide" data-color="1">1</div>
      <div class="odo-carousel__slide" data-color="2">2</div>
      <div class="odo-carousel__slide" data-color="3">3</div>
      <div class="odo-carousel__slide" data-color="4">4</div>
      <div class="odo-carousel__slide" data-color="5">5</div>
    </div>
  </div>
</div>

Setup

The only extra option with fading carousels is crossfadeAmount, which is a number between [inclusive] 0.0 and 1.0. A crossfade of 1.0 means that both slides will fade at the same time. A crossfade of zero means the previous slide will wait until the next slide has completely faded in before it fades out.

var darthFader = new OdoCarousel(document.getElementById('darth-fader'), {
  isFade: true,
  isLooped: true
});

Events

Odo Carousel emits 3 events: WILL_NAVIGATE, SLIDE_START, and SLIDE_END. If the preventDefault method is called on the event object from the WILL_NAVIGATE event, it will prevent the carousel from navigating. The SLIDE_START and SLIDE_END events contain useful properties for customizing your own carousel: event.from index, event.to index, and event.hasSlideChanged (the slide could animate back to the closest slide instead of going to a new one).

var regular = new OdoCarousel(document.getElementById('regular'));

regular.on(OdoCarousel.EventType.SLIDE_END, function (event) {
  console.log(event);
});

Options

These are the default options for the carousel. They can all be overridden.

Defaults: {
  startIndex: 0,
  isVertical: false,
  isLooped: false,
  isJumped: false,
  isFade: false,
  isCentered: false,
  neighborCount: 1,
  slideshowSpeed: 1000,
  animationSpeed: 400,
  crossfadeAmount: 0.875,
  easing: 'cubic-bezier(0.250, 0.460, 0.450, 0.940)',
  pagination: false,
  getNavPaddleHtml: null,
  getPaginationHtml: null,
  template: {
    paddles: '<nav class="odo-carousel__nav-paddles">{{ prev }}{{ next }}</nav>',
    paddleNext: '<a href="javascript:void(0)" class="odo-carousel__nav-paddle odo-carousel__nav-next">{{ paddleInner }}</a>',
    paddlePrev: '<a href="javascript:void(0)" class="odo-carousel__nav-paddle odo-carousel__nav-prev">{{ paddleInner }}</a>',
    paddleNextInner: '<svg viewBox="75.4 27 461.2 738"><path d="M167.7 27l368.9 369-368.9 369-92.3-92.3 276.7-276.7-276.7-276.7z"/></svg>',
    paddlePrevInner: '<svg viewBox="75.396 26.994 461.208 738.012"><path d="M444.336 765.006l-368.94-369.006 368.94-369.006 92.268 92.268-276.738 276.738 276.738 276.738z"/></svg>',
    pagination: '<nav class="odo-carousel__pagination">{{ dots }}</nav>',
    paginationDot: '<a href="javascript:void(0)" class="odo-carousel__pagination-dot" data-index="{{ index }}"></a>',
    paginationDotSecondary: '<a href="javascript:void(0)" class="odo-carousel__pagination-dot" data-index="{{ index }}" data-secondary-index="{{ secondaryIndex }}" data-hidden="{{ hidden }}"></a>',
  },
}

Templates

The carousel uses a simple template engine to replace double curlies with data for the paddles and pagination. You can either change a single template with the the template options, or completely change the structure using getNavPaddleHtml and getPaginationHtml. Here’s a few examples of how the templates work which you can paste in the console.

OdoCarousel.template("Today is {{ day }}", {
  day: 'Friday'
}); // "Today is Friday"

OdoCarousel.template("Today is {{ month.day }}", {
  month: {
    day: "Friday"
  }
}); // "Today is Friday

OdoCarousel.template("Today is {{ day }}", {
  dayOfTheWeek: 'Friday',
  day: function () {
    return this.dayOfTheWeek;
  }
}); // "Today is Friday"

To customize the structure of paddles or pagination, use the getNavPaddleHtml and getPaginationHtml methods, respectively. Like the Vertical and Looped example, you need to return a string for the carousel to use (it can be empty). Click events on the carousel are delegated, so anything inside the main carousel element with the pagination dot class or next/previous class will trigger the carousel to navigate.