JSFiddle - React, Tailwind, and code Playground

by timwhitlock

HTML

<div class="wrap" id="testwrap">
    <div class="grid"> 
        <a href="#" class="item">1</a> 
        <a href="#" class="item">2</a> 
        <a href="#" class="item">3</a> 
        <a href="#" class="item">4</a> 
        <a href="#" class="item">5</a> 
        <a href="#" class="item">6</a> 
        <a href="#" class="item">7</a> 
        <a href="#" class="item">8</a> 
        <a href="#" class="item">9</a> 
        <a href="#" class="item">10</a> 
        <a href="#" class="item">11</a> 
        <a href="#" class="item">12</a> 
        <a href="#" class="item">13</a> 
        <a href="#" class="item">14</a> 
        <a href="#" class="item">15</a> 
        <a href="#" class="item">16</a> 
        <a href="#" class="item">17</a> 
        <a href="#" class="item">18</a> 
        <a href="#" class="item">19</a> 
        <a href="#" class="item">20</a> 
    </div>        
</div>

CSS

body {
    margin: 0;
    font: 11px Arial;
}

.wrap{
    width: 100%;
    max-width: 738px;
    height: 110px;
    overflow-x: scroll;
    white-space: nowrap;
    background: #f5f5f5;
}

.grid {
    width: 3740px; /* 20 x 187 */
}

.grid a {
    background: #ccc;
    text-decoration: none;
    color: #333;
    font-size: 20px;
    display: block;
    float: left;
    text-align: center;
    vertical-align: middle;
    width: 137px;
    height: 50px;
    padding: 20px;
    margin: 0 10px 0 0;
}

.grid a:nth-child(2n+2) {
    background-color: #ddd;
}

.grid a:first-child,
.grid a:last-child {
    background-color: #666;
    color: #fff;
}

.grid a.clicked {
    color: #fff;
    background-color: #cc0000;
}


/**
 * Progressive enhancement for IE10 touch properties
 */

.wrap {
    -ms-scroll-snap-points-x: snapInterval( 0px, 187px );
    -ms-scroll-snap-type: mandatory;
    -ms-scroll-chaining: none;
}

JavaScript

/**
 * Add swipe/scroll effect for touch enabled browsers except IE10 which will use CSS only
 */



/**
 * Scroll animator using setInterval
 */
function initScroller( el, onEnd ){
    var t,             // <- timeout id
        ppf,           // <- pixels per frame
        maxFunc,       // <- Math.max, or Math.min depending on direction of scroll
        scrollNow,     // <- current element scrollLeft
        scrollTarget,  // <- target scrollLeft
        interval = 16; // <- 60fps
    function onInterval(){  
        scrollNow = Math.max( 0, maxFunc( scrollTarget, scrollNow + ppf ) );
        el.scrollLeft = scrollNow;
        if( scrollTarget === scrollNow ){
            stop();
            onEnd && onEnd();
        }
    }
    function start(){
        stop();
        t = setInterval( onInterval, interval );
    }
    function stop(){
        t && clearInterval(t);
        t = null;
    }
    // expose public functions, stop and scroll
    return { 
        stop: stop, 
        scroll: function ( scrollLeft, speed ){
            scrollTarget = scrollLeft;
            scrollNow = el.scrollLeft;
            var direction = scrollTarget > scrollNow ? 1 : -1;
            maxFunc = Math[ direction === 1 ? 'min' : 'max' ];
            ppf = Math.round( speed * interval * direction );
            start();
        }
    };
}      





/**
 * Enhance with touchstart and touchend for swipe inertia
 */
function initSwiper( el, onStart, onEnd ){
    var xstart  = 0,  // <- initial scroll position on first touch
        tstarts = [], // <- time each touch started
        xoffset = [], // <- initial x position of each touch
        yoffset = []; // <- initial y position of each touch

    /**
     * Utility for getting current time in milliseconds
     */
    var now = Date.now || function(){
        return new Date().getTime();
    };        

    /**
     * Initialize swipe on touchstart
     */
    function onTouchStart( event ){
        var t = now();
       ...