Kineto

About Kineto

Kineto is an open source JavaScript library for building carousels on websites. It aims to cover every essential feature of a carousel while staying simple and intuitive for integration.

  • No extra dependencies required.
  • Works in all major browsers and IE9+.
  • Control over size, spacing, direction of the slides.
  • Infinite loop without duplicate slides.
  • Add or remove slides dynamically.
  • Custom speed and easing of the movement.
  • Navigation with touch and mouse swipe.
  • Automatic rotation & sync with other Kineto instances.
  • And many more.

Get the latest release Report issue

Get started

Please download the latest version of Kineto. Ready-to-use resources are located in /dist folder.

Release page

Embed Kineto assets

Insert the CSS and JS files into your HTML.

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="kineto.css" />
  </head>
  <body>
    <script src="kineto.js"></script>
  </body>
</html>
  • The CSS file contains opinionated theme for Kineto's submodules. You don't need to include it if you are going to create your own look and feel.
  • The JS file is placed at the end of body element for improved performance.

Create HTML structure

You only need to have a container holding your content slides as direct children elements. class and accessibility attributes will be automatically added once you enable Kineto.

<div>
  <div>Slide 1</div>
  <div>Slide 2</div>
  <div>Slide 3</div>
</div>

Initialize Kineto with JavaScript

Once DOM is ready, you can now initialize Kineto with Kineto.create() method. Below is an example:

Kineto.create('#carousel', { pagination: true });

Kineto is applied to all elements matching the selector provided. To control only a specific instance, we recommend you to use either a unique selector or an element object instead the selector.

Demo

Here are some example code recipes for different use cases. For more details, please read options section.

Default

Kineto.create('#carousel');

Number of visible slides

Kineto.create('#carousel', { perView: 2 });

Margin between slides

Kineto.create('#carousel', { margin: 40 });

Vertical direction

Kineto.create('#carousel', { mode: 'vertical' });

Infinite loop

Kineto.create('#carousel', { loop: true });

Adaptive Height

Kineto.create('#carousel', { height: 'adaptive' });

Equal Height

Kineto.create('#carousel', { height: 'equal' });

Move by 2

Kineto.create('#carousel', { moveBy: 2 });

Movement speed

Kineto.create('#carousel', { speed: 1000 });

Animation easing

Kineto.create('#carousel', { easing: 'easeInOutBack' });
Kineto.create('#carousel', { easing: [0.2, 2, 0.3, 0.8] });

Move to clicked slide

Kineto.create('#carousel', { moveOnClick: true });

Auto playing

Kineto.create('#carousel', { stream: true });

Navigation components

Kineto.create('#carousel', {
  arrows: true,
  arrowTemplate: ['←', '→'],
  count: true,
  countTemplate: '<em class="current">{{current}}</em> / {{total}}',
  pagination: true,
  paginationTemplate: '{{index}}',
});

Block swiping beyond edges

Kineto.create('#carousel', { swipeEdgeBounce: false });

Mouse wheel

Kineto.create('#carousel', { wheel: true });

Allow only 1 wheel per second

Kineto.create('#carousel', { wheel: true, wheelThrottle: 1000 });

Scroll blocked after hitting edges

Kineto.create('#carousel', { wheelEdgeRelease: false, mode: 'vertical' });

Use percentage values in CSS

Setting responsive option make carousels reload on viewport size changed. If you want them to scale with its container without triggering reload, usePercent will come in handy. (Resize your browser window to observe the difference.)

Kineto.create('#carousel', { usePercent: true, responsive: false });

Responsive

Kineto.create('#carousel', {
  // 0 ~ 599
  perView: 1,
  responsive: {
    // 600 ~ 799
    600: {
      perView: 1.5,
      margin: 20,
    },
    // 800 ~
    800: {
      perView: 2,
      margin: 30,
    },
  }
});

The responsive option follows mobile-first approach — it is processed from the smallest resolution to the largest. Using desktop-first responsiveMode can reverse the order of processing.

Kineto.create('#carousel', {
  // 801 ~
  perView: 2,
  responsive: {
    // 601 ~ 800
    800: {
      perView: 1.5,
      margin: 30,
    },
    // 0 ~ 600
    600: {
      perView: 1,
      margin: 20,
    },
  },
  responsiveMode: 'desktop-first'
});

Sync between instances

// Assign same `syncId` to carousels to get in sync.
Kineto.create('#carousel1', {
  syncId: 'gallery',
  loop: true,
  stream: true,
});

Kineto.create('#carousel2', {
  syncId: 'gallery',
  perView: 3,
  loop: true,
  pagination: false,
});

Kineto.create('#carousel3', {
  syncId: 'gallery',
  perView: 5,
  align: 'justify',
  arrows: false,
  pagination: false,
});

Options

Kineto.create method takes 2 parameters — you can configure Kineto by passing an object containing desired options.

Kineto.create(container, options);

Here's the summarized version of default options.

{
  mode: 'horizontal',
  align: 'center',
  perView: 'auto',
  height: 'auto',
  margin: 10,
  loop: false,
  startAt: 0,
  moveBy: 1,
  speed: 600,
  easing: [0, 0, 0.2, 1],
  moveOnClick: false,
  waitAnimation: false,
  stream: false,
  streamEvery: 3000,
  streamRewind: true,
  pauseOnFocus: true,
  pauseOnHover: false,
  syncId: null,
  arrows: true,
  arrowsInto: null,
  arrowTemplate: null,
  count: false,
  countInto: null,
  countTemplate: null,
  pagination: true,
  paginationInto: null,
  paginationTemplate: null,
  touchSwipe: true,
  mouseSwipe: true,
  swipeThreshold: 3,
  swipeMultiplier: 1,
  swipeEdgeBounce: true,
  swipeEdgeFriction: 0.8,
  wheel: false,
  wheelTarget: null,
  wheelThrottle: 'auto',
  wheelEdgeRelease: true,
  aria: true,
  cssPrecision: 3,
  usePercent: false,
  responsive: true,
  responsiveDelay: 100,
  responsiveMode: 'mobile-first',
}

Layout

Key Type Default Description
mode 'horizontal'
'vertical'
'horizontal' The direction of the content flow. (The option is named so to prepare for the addition of new modes in the future.)
align 'center'
'start'
'end'
'justify'
'center'

The alignment of slides.

  • 'center': Centerize the current slide, leaving empty space on both sides if necessary.
  • 'start': Align slides to the beginning point, leaving empty space after the last slide if necessary.
  • 'end': Align slides to the ending point, leaving empty space before the first slide if necessary.
  • 'justify': Align slides to both sides, leaving no empty space.
perView number
'auto'
'auto' Number of slides to show per frame. Floats are accepted.
height 'auto'
'adaptive'
'equal'
'auto'

The way the carousel handles its height.

  • 'auto': Scale the container to fit the tallest slide's height; the height of slides do not change.
  • 'adaptive': Adjust the container's height to fit that of currently active slide.
  • 'equal': Equalize height of the slides to the tallest one.
margin number 10 Distance (in pixel) between slides.
loop boolean false Wrap around to the other end on edges to allow infinite navigation.
startAt number 0 Zero-based index of the slide to be shown on initialization.

Movement

Options related to movements of the carousel. They do not affect swipe movements.

Key Type Default Description
moveBy number 1 Number of slides to pass when moving.
speed number 600 Duration of movements.
easing string
number[]
[0, 0, 0.2, 1]

Easing curve of movements. You can enter either a CSS-like easing function name listed below, or an array of 4 besier points.

  • linear
  • ease
  • easeIn
  • easeOut
  • easeInOut
  • easeInSine
  • easeOutSine
  • easeInOutSine
  • easeInQuad
  • easeOutQuad
  • easeInOutQuad
  • easeInCubic
  • easeOutCubic
  • easeInOutCubic
  • easeInQuart
  • easeOutQuart
  • easeInOutQuart
  • easeInQuint
  • easeOutQuint
  • easeInOutQuint
  • easeInExpo
  • easeOutExpo
  • easeInOutExpo
  • easeInCirc
  • easeOutCirc
  • easeInOutCirc
  • easeInBack
  • easeOutBack
  • easeInOutBack
moveOnClick boolean false Move to clicked slide.
waitAnimation boolean false Prevent movements from being fired while an animation being played.

Streaming

Key Type Default Description
stream boolean false

Automatically move to next slide every N milliseconds.

Kineto will pause streaming when:

  • The user tries to navigate with arrow or pagination buttons. (The stream will resume after the button loses the focus.)
  • The user leaves the tab or the browser. (This feature works in browsers that supports Page Visibility API.)
  • pauseOnFocus or pauseOnHover option is active, and the user performs relevant interaction.
streamEvery number 3000 Time gap (in milliseconds) between each stream tick.
streamRewind boolean true Redirect the stream to the first slide after hitting the last slide.
pauseOnFocus boolean true Pause the streaming while slide is focused.
pauseOnHover boolean false Pause the streaming while pointer cursor is on.
syncId string null

Synchronize other kineto instances with same syncId.

Setting stream: true to any of those instances will enable streaming of the 1st instance, not the rest.

Addons

Key Type Default Description
arrows boolean true Add previous & next buttons.
arrowsInto string
HTMLElement
null Specify the container element for the arrow buttons. Accepts either an element selector or a element object.
arrowsTemplate [string, string] null

Array of strings representing content HTML for the arrow buttons.

e.g. [<span>&larr;</span>, <span>&rarr;</span>]

count boolean true Add a fraction-style pagination.
countInto string null Specify the container element for the count component. Accepts either an element selector or a element object.
countTemplate string null

String representing content HTML for the count component.

  • {{current}} will be replaced with current page.
  • {{total}} will be replaced with number of total pages.

e.g. <span>{{current}}</span> / {{total}}

pagination boolean true Add page indicator buttons.
paginationInto string null Specify the container element for the pagination. Accepts either an element selector or a element object.
paginationTemplate string null

String representing content HTML for the count component.

  • {{index}} will be replaced each page number.

e.g. <span>{{index}}</span>

Swipe

Key Type Default Description
touchSwipe boolean true Allow scrolling by touch drag.
mouseSwipe boolean true Allow scrolling by mouse drag.
swipeThreshold number 3 Amount of pixels in pixel to skip before drag movement starts.
swipeMultiplier number 1 Amplify swipe movement by this value.
swipeEdgeBounce boolean true Apply elastic rebound motion on both edges. Has no effect when loop: true
swipeEdgeFriction boolean 0.8 Reduce the swipe movement beyond both edges. Any number between 0 ~ 1(inclusive) is accepted.

Mouse wheel

Key Type Default Description
wheel boolean false Allow scrolling by mouse wheel.
wheelTarget string null Element that receives the mouse wheel. Fallback to the element wrapper slides.
wheelThrottle number
'auto'
'auto' Time (in miliseconds) during which successive scroll attempts should be ignored.
wheelEdgeRelease boolean true

Unblock scroll when the scroll reaches the last slide in the scrolling direction.

Especially useful when the carousel's content flow is parallrel to the document's scroll direction.

Miscellaneous

Key Type Default Description
aria boolean true Add & update accessibility attributes.
cssPrecision number 3 The precision for CSS values that include numbers. Setting it to 0 may fix issues with half-pixel rendering.
usePercent boolean false

Use percentage for CSS values that include numbers.

Useful if you want your carousel to scale with its container without reloading.

Responsive

Key Type Default Description
responsive boolean
object
true

Switch kineto settings based on viewport width.

The value has to be an object type: child keys stand for viewport range, child values kineto settings matching the range.

Kineto.create('#carousel', {
  // 0 ~ 599
  perView: 1,
  responsive: {
    // 600 ~ 799
    600: {
      perView: 1.5,
      margin: 20,
    },
    // 800 ~
    800: {
      perView: 2,
      margin: 30,
    },
  }
});
responsiveDelay number 100 Time delay (in milliseconds) before responding to the viewport changes.
responsiveMode 'mobile-first'
'desktop-first'
'mobile-first'

The strategy used to parse responsive option.

  • 'mobile-first': Default resolution to 0. The resolutions passed are processed in ascending order.
  • 'desktop-first': Default resolution to Number.MAX_SAFE_INTEGER. The resolutions passed are processed in descending order.
Kineto.create('#carousel', {
  // 801 ~
  perView: 2,
  responsive: {
    // 601 ~ 800
    800: {
      perView: 1.5,
      margin: 30,
    },
    // 0 ~ 600
    600: {
      perView: 1,
      margin: 20,
    },
  },
  responsiveMode: 'desktop-first'
});

ClassNames

The plugin dispose Kineto.setClassFormat() to enable the customization of how CSS classes are formatted, to help you stay constant in class naming throughout the project.

You can either provide a built-in preset name, or a custom formatter created on your own.

kebabcase (default)

Join words with dash(-), seperating each kind of identifier for higher modularity.

  • 'kineto': The container.
  • 'kineto-animating': Dynamically added class to the container during animations.
  • 'kineto-slide': The slides inside the container.
  • 'kineto-slide kineto-visible kineto-active': The currently active and visible slide.
Kineto.setClassFormat('kebabcase');

BEM

Follow BEM strategy for class naming. BEM helps in reducing specificity down to a minimum, and writing codes with high readability. Here's some examples how classes will look like:

  • 'kineto': The container.
  • 'kineto--animating': Dynamically added class to the container during animations.
  • 'kineto__slide': The slides inside the container.
  • 'kineto__slide--visible kineto__slide--active': The currently active and visible slide.
Kineto.setClassFormat('BEM');

Custom formatter function

You can make your own formatter using the raw data passed as parameters. The formatter function will receive block, element, modifier according to BEM rules.

  • @param block string
  • @param elements string[]
  • @param modifier string
Kineto.setClassFormat(function(block, elements, modifier) {
  var output = capitalize(block);
  if (elements.length) {
    output += '_' + elements.join('_');
  }
  if (modifier) {
    output += '-' + modifier;
  }
  return output;
});

Methods

The plugin dispose public methods in its namespace. Most of them require follow next syntax:

Kineto.method(container, [, arguments...]);
  • container — The selector for the container(s) to execute the command onto; a HTMLElement, a NodeList, a HTMLCollection, or an Array of HTMLElement can be used alternatively.
  • arguments — Extra parameters to pass to the method.

.setDefaults()

Set default options for all Kineto instance to be created after this line of code. Does not require container element as parameter.

  • @param options object — Subset of Kineto options to use as base options.
// Use next settings for all Kineto instances after this line.
Kineto.setDefaults({
  stream: true,
  streamEvery: 4000,
});

.setClassFormat()

Set CSS class formatting rules for all Kineto instance to be created after this line of code.

  • @param format string | function — A class naming preset or a custom formatter function.

Read ClassNames section for the details.

// Use BEM-based classes for all Kineto instances after this line.
Kineto.setClassFormat('BEM');

.create()

Instantiate Kineto on elements that match the selector passed.

  • @param container string | HTMLElement — The container element or its selector.
  • @param options object — Kineto options. See Options for details.
// Seletor (single or multiple elements)
Kineto.create('.carousel');

// HTMLElement (single element)
var element = document.querySelector('.carousel');
Kineto.create(element);

// NodeList (multiple elements)
var nodelist = document.querySelectorAll('.carousel');
Kineto.create(nodelist);

// HTMLCollection (multiple elements)
var collection = document.getElementsByClassName('.carousel');
Kineto.create(collection);

// Array of HTMLElement (multiple elements)
var elementArray = [].slice.call(document.querySelectorAll('.carousel'));
Kineto.create(elementArray);

// Using jQuery
var fromJquery = $('.carousel').get();
Kineto.create(fromJquery);

.goTo()

Move to a specific slide that matches the index passed. Negative index will count from the end.

  • @param container string | HTMLElement — The container element or its selector.
  • @param index number — The index of the destination slide.
// Go to the 3rd slide.
Kineto.goTo('#carousel', 2);

// Go to the last slide.
Kineto.goTo('#carousel', -1);

.next()

Go to the next slide.

  • @param container string | HTMLElement — The container element or its selector.

.prev()

Go to the previous slide.

  • @param container string | HTMLElement — The container element or its selector.

.refresh()

Force Kineto reload and recalculate layout. This is helpful when manipulating slide content that affects the layout.

  • @param container string | HTMLElement — The container element or its selector.

.addSlide()

Add new slide(s) to an existing carousel. Negative index will select from the end. If inserting position is not specified, add the slide at the end.

  • @param container string | HTMLElement — The container element or its selector.
  • @param contents string | string[] — HTML string(s) representing the content the new slide(s).
  • @param position number — Optional. The position where the new slide(s) should be inserted at.
// Add 1 slide at the end
Kineto.addSlide('#carousel', 'New slide');

// Add 1 slide after the 2nd slide
Kineto.addSlide('#carousel', 'New slide', 1);

// Add 2 slides at the end
Kineto.addSlide('#carousel', ['New slide', 'Newer slide']);

// Add 2 slides after the 2nd last slide
Kineto.addSlide('#carousel', ['New slide', 'Newer slide'], -2);

.removeSlide()

Remove existing slide at a specific position. If removing position is not specified, remove the last slide.

  • @param container string | HTMLElement — The container element or its selector.
  • @param contents string | string[] — HTML string(s) representing the content the new slide(s).
// Remove last slide
Kineto.removeSlide('#carousel');

// Remove 2nd slide
Kineto.removeSlide('#carousel', 1);

// Remove 2nd last slides
Kineto.removeSlide('#carousel', -2);

// Remove last 2 slides
Kineto.removeSlide('#carousel', -2, 2);

.replaceSlides()

Replace existing slides with new ones. (Please note that the method name is plural.) If replacing slides are not specified, empty the carousel.

  • @param container string | HTMLElement — The container element or its selector.
  • @param position number — Optional. The index of the slide to remove. @param deleteCount number — Optional. The number of slides to remove from position.
// Remove all slides
Kineto.replaceSlides('#carousel');

// Remove all slides and insert a new slide
Kineto.replaceSlides('#carousel', 'new');

// Remove all slides and insert 2 new slides
Kineto.replaceSlides('#carousel', ['new1', 'new2']);

.play()

Move to the next slide periodically. (The time interval between each tick can be configured through streamEvery option.)

After hitting the last slide, return to the first one. (You can choose not to do so by setting streamRewind option to false.

.pause()

Stop moving to the next slide.

.on()

Bind a listener to Kineto's internal event. Read Events for more details.

  • @param container string | HTMLElement — The container element or its selector.
  • @param type string — The name of the event to listen to.
  • @param listener function — The event handler to bind.
// Do something when `#carousel` completes a movement.
Kineto.on('#carousel', 'changed', function(currentIndex, previousIndex) {
  // ...
});

.off()

Unbind a listener that has been bound through .on() method above. Read Events for more details. The type and the listener should be identical to those used in binding process.

  • @param container string | HTMLElement — The container element or its selector.
  • @param type string — The name of the event to remove.
  • @param listener function — The event handler to unbind.
// Handler for gallery change
function handleGalleryChange(currentIndex, previousIndex) { }

// Bind the handler
Kineto.on('#carousel', 'changed', handleGalleryChange);

// Unbind the handler
Kineto.off('#carousel', 'changed', handleGalleryChange);

Events

The event listeners you registered using Kineto.on() method, are invoked every time Kineto emits relevant event.

// Register an event listener
Kineto.on(container, type, listener);

// Unregister an event listener
Kineto.off(container, type, listener);

Some of them receives extra information about the event occured; please find the details below.

init

Event fired on completion of the initialization process. The handler should be added before the initialization.

Kineto
  .on('#carousel', 'init', function() {}) // Bind the listener first
  .create('#carousel'); // Then init

destroy

Event fired at the beginning of destruction of the instance. The handler should be added beforehand.

Kineto
  .on('#carousel', 'destroy', function() {})
  .create('#carousel');

change

  • @param nextIndex number — The index of slide that we are moving towards.
  • @param currentIndex number — The index of currently active slide before moving.

Event fired before moving to another slide.

Kineto.on('#carousel', 'change', function(nextIndex, currentIndex) {});

changed

  • @param currentIndex number — The index of currently active slide after moving.
  • @param previousIndex number — The index of previously active slide.

Event fired after moving to another slide.

Kineto.on('#carousel', 'change', function(currentIndex, previousIndex) {});

FAQ

Was this helpful to you? If you're experiencing a different issue, feel free to open a thread on GitHub issues.

The name "Kineto" comes from the word kinetoscope. We find a carousel's behavior similar to that of a kinetoscope; showing a fraction of the content while having more to discover behind the frame.

The inner container of a "Kineto" instance determining which sequence should be shown is called "scope".

The most common reason of broken layouts comes from a hidden ancestor element. Kineto requires the container element to be visible at the moment of initialization, to be able to measure the size of elements that make up the carousel.

You can solve the issue either by calling Kineto.reload() or lazy-init Kineto when the parent element gets visible.

// Example 1. Force-refresh Kineto on demand
modal.on('open', function() {
  Kineto.reload('.carousel-inside-modal');
});
// Example 2. Init Kineto on demand
modal.on('open', function() {
  Kineto.create('.carousel-inside-modal');
});