Fork me on GitHub

Touch enabled minimalistic slider written in vanilla JavaScript

Hardware accelerated transitions

Usable as a jQuery plugin

Options for custom easing effects

infinite looping ~ carousel

No compromises for fallbacks

Written in ecmascript 6

Using webpack, babel & eslint for development

Download Here

Prerequisited Html structure
Class names can be changed through options


            <div class="slider js_slider">
                <div class="frame js_frame">
                    <ul class="slides js_slides">
                        <li class="js_slide">1</li>
                        <li class="js_slide">2</li>
                        <li class="js_slide">3</li>
                        <li class="js_slide">4</li>
                        <li class="js_slide">5</li>
                        <li class="js_slide">6</li>
                    </ul>
                </div>
                <span class="js_prev prev">
                    <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 501.5 501.5"><g><path fill="#2E435A" d="M302.67 90.877l55.77 55.508L254.575 250.75 358.44 355.116l-55.77 55.506L143.56 250.75z"/></g></svg>
                </span>
                <span class="js_next next">
                    <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 501.5 501.5"><g><path fill="#2E435A" d="M199.33 410.622l-55.77-55.508L247.425 250.75 143.56 146.384l55.77-55.507L358.44 250.75z"/></g></svg>
                </span>
            </div>
            

Prerequisited CSS styles


            /**
             * (optional) define here the style definitions which should be applied on the slider container
             * e.g. width including further controls like arrows etc.
             */
            .slider {}

            .frame {
                /**
                 * (optional) wrapper width, specifies width of the slider frame.
                 */
                width: 880px;

                position: relative;
                font-size: 0;
                line-height: 0;
                overflow: hidden;
                white-space: nowrap;
            }

            .slides {
                display: inline-block;
            }

            li {
                position: relative;
                display: inline-block;

                /**
                 * (optional) if the content inside the slide element has a defined size.
                 */
                width: 880px;
            }

            .prev, .next {
                position: absolute;
                top: 50%;
                margin-top: -25px;
                display: block;
                cursor: pointer;
            }

            .next {
                right: 0;
            }

            .prev {
                left: 0;
            }

            .next svg, .prev svg {
                width: 25px;
            }
            

Single element - Fixed width [infinite]

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

    document.addEventListener('DOMContentLoaded', function () {
        var simple = document.querySelector('.js_simple');

        lory(simple, {
            infinite: 1
        });
    });

Single element - dot navigation

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

    document.addEventListener('DOMContentLoaded', function () {
        var simple_dots       = document.querySelector('.js_simple_dots');
        var dot_count         = simple_dots.querySelectorAll('.js_slide').length;
        var dot_container     = simple_dots.querySelector('.js_dots');
        var dot_list_item     = document.createElement('li');

        function handleDotEvent(e) {
            if (e.type === 'before.lory.init') {
              for (var i = 0, len = dot_count; i < len; i++) {
                var clone = dot_list_item.cloneNode();
                dot_container.appendChild(clone);
              }
              dot_container.childNodes[0].classList.add('active');
            }
            if (e.type === 'after.lory.init') {
              for (var i = 0, len = dot_count; i < len; i++) {
                dot_container.childNodes[i].addEventListener('click', function(e) {
                  dot_navigation_slider.slideTo(Array.prototype.indexOf.call(dot_container.childNodes, e.target));
                });
              }
            }
            if (e.type === 'after.lory.slide') {
              for (var i = 0, len = dot_container.childNodes.length; i < len; i++) {
                dot_container.childNodes[i].classList.remove('active');
              }
              dot_container.childNodes[e.detail.currentSlide - 1].classList.add('active');
            }
            if (e.type === 'on.lory.resize') {
                for (var i = 0, len = dot_container.childNodes.length; i < len; i++) {
                    dot_container.childNodes[i].classList.remove('active');
                }
                dot_container.childNodes[0].classList.add('active');
            }
        }
        simple_dots.addEventListener('before.lory.init', handleDotEvent);
        simple_dots.addEventListener('after.lory.init', handleDotEvent);
        simple_dots.addEventListener('after.lory.slide', handleDotEvent);
        simple_dots.addEventListener('on.lory.resize', handleDotEvent);

        var dot_navigation_slider = lory(simple_dots, {
            infinite: 1,
            enableMouseEvents: true
        });
    });

Single element - Percentage width [infinite]

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

    document.addEventListener('DOMContentLoaded', function () {
        var percentage = document.querySelector('.js_percentage');

        lory(percentage, {
            infinite: 1
        });
    });

Single element - Fixed width [rewind]

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

    document.addEventListener('DOMContentLoaded', function () {
        var rewind = document.querySelector('.js_rewind');

        lory(rewind, {
            rewind: true
        });
    });

Single element - Percentage width [rewind]

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

    document.addEventListener('DOMContentLoaded', function () {
        var rewind_percentage = document.querySelector('.js_rewind_percentage');

        lory(rewind_percentage, {
            rewind: true
        });
    });

Variable width

  • 220
  • 190
  • 150
  • 130
  • 250
  • 180
  • 200
  • 140
  • 120
  • 240

    document.addEventListener('DOMContentLoaded', function () {
        var variableWidth = document.querySelector('.js_variablewlidth');

        lory(variableWidth, {
            rewind: true
        });
    });

Multiple slides to scroll

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

    document.addEventListener('DOMContentLoaded', function () {
        var multiSlides = document.querySelector('.js_multislides');

        lory(multiSlides, {
            infinite: 4,
            slidesToScroll: 4
        });
    });

Custom easings

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

    document.addEventListener('DOMContentLoaded', function () {
        var ease = document.querySelector('.js_ease');

        // http://easings.net/

        lory(ease, {
            infinite: 4,
            slidesToScroll: 4,
            slideSpeed: 1000,
            ease: 'cubic-bezier(0.455, 0.03, 0.515, 0.955)'
        });
    });

Showing events

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

    document.addEventListener('DOMContentLoaded', function () {
        var events = document.querySelector('.js_events');

        function handleEvent(e) {
            var newSpan = document.createElement('span');
            var time = new Date();
            time = time.getHours() + ':' + time.getMinutes() + ':' + time.getSeconds() + ',' + time.getMilliseconds();
            var newContent = document.createTextNode('[' + time + '] Event dispatched: "' + e.type + '"');
            newSpan.appendChild(newContent);
            e.target.nextElementSibling.appendChild(newSpan);
        }

        events.addEventListener('before.lory.init', handleEvent);
        events.addEventListener('after.lory.init', handleEvent);
        events.addEventListener('before.lory.slide', handleEvent);
        events.addEventListener('after.lory.slide', handleEvent);
        events.addEventListener('before.lory.destroy', handleEvent);
        events.addEventListener('after.lory.destroy', handleEvent);

        events.addEventListener('on.lory.resize', handleEvent);
        events.addEventListener('on.lory.touchend', handleEvent);
        events.addEventListener('on.lory.touchmove', handleEvent);
        events.addEventListener('on.lory.touchstart', handleEvent);

        lory(events, {
            infinite: 1
        });
    });

initialize multiple similiar sliders


        'use strict';

        document.addEventListener('DOMContentLoaded', function () {
            Array.prototype.slice.call(document.querySelectorAll('.js_slider')).forEach(function (element, index) {
                lory(element, {});
            });
        });

Consume it as a commonJS module


    "use strict";

    var lory = require('lory.js').lory;

    document.addEventListener('DOMContentLoaded', function() {
        var slider = document.querySelector('.js_slider');

        lory(slider, {
            // options going here
        });
    });

Consume it as an ES2015 module


    import {lory} from 'lory.js';

    document.addEventListener('DOMContentLoaded', () => {
        const slider = document.querySelector('.js_slider');

        lory(slider, {
            // options going here
        });
    });

Use it as a jQuery Plugin


        <script src="js/jquery.lory.min.js"></script>

        'use strict';

        var $elements = $('.js_simple');

        $(function() {
            $elements.lory({
                infinite: 1
            });

            $elements.data().lory.slideTo(4);
            // $elements.data().lory.prev();
            // $elements.data().lory.next();
        });

Local development


        // To install dev dependencies run:

        npm install

        // To start the development server run:

        npm run dev

        // To lint your code run:

        npm run lint

        // To make a full new build run:

        npm run build

Run tests


        // To install dev dependencies run:

        npm install

        // To start the karma tests locally run:

        npm run karma-local

Public API

prev: slides to the previous slide
next: slides to the next slide
slideTo: slides to the index given as an argument: (arguments: index {number})
returnIndex: returns the index of the current slide element
setup: binds eventlisteners, merging default and user options, setup the slides based on DOM (called once during initialisation) Call setup if DOM or user options have changed or eventlisteners needs to be rebinded.
reset: sets the slider back to the starting position and resets the current index (called on Resize event)
destroy: unmount/destroy the instance of lory

Options

slidesToScroll slides scrolled at once default: 1
enableMouseEvents enabled mouse events default: false
infinite like carousel, works with multiple slides. (do not combine with rewind) default: false (number of visible slides)
rewind if slider reached the last slide, with next click the slider goes back to the startindex. (do not combine with infinite) default: false
slideSpeed time in milliseconds for the animation of a valid slide attempt default: 300
rewindSpeed time in milliseconds for the animation of the rewind after the last slide default: 600
snapBackSpeed time for the snapBack of the slider if the slide attempt was not valid default: 200
ease cubic bezier easing functions: http://easings.net/de default: 'ease'
classNameFrame class name for slider frame default: 'js_frame'
classNameSlideContainer class name for slides container default: 'js_slides'
classNamePrevCtrl class name for slider previous control default: 'js_prev'
classNameNextCtrl class name for slider next control default: 'js_next'

Events

before.lory.init fires before initialisation (first in setup function)
after.lory.init fires after initialisation (end of setup function)
before.lory.slide arguments: currentSlide, nextSlide fires before slide change
after.lory.slide arguments: currentSlide fires after slide change
before.lory.destroy fires before the slider instance gets destroyed
after.lory.destroy fires after the slider instance gets destroyed
on.lory.resize fires on every resize event
on.lory.touchstart fires on every slider touchstart event
on.lory.touchmove fires on every slider touchmove event
on.lory.touchend fires on every slider touchend event