Fork me on GitHub

Why jcr

Because the carousels I tried didn't do exactly what I needed and customizing them was so painful. After a couple of days trying to integrate 5 or 6 different carousels into a project I worked on without fruitful result, I gave up and coded the initial version of jcr in a few hours. Over the course of a few weeks, jcr has grown to have a lot more features than I initially needed and I thought it might benefit other people.

Feature highlights

  • Single-minded: a minimally programmable horizontal carousel, that's it
  • Responsive to however-styled containing HTML element, child elements and browser resize
  • CSS-driven UI, easy to customize by overriding default styles
  • Small, ~3KB of gzipped and minified JavaScript and CSS
  • Tested on Chrome, Firefox, Safari 5/6/Mobile, Internet Explorer 9/10

Show me

Below is a simple carousel activated with default options and two built-in plugins (more on plugins later).

$(function() {
    var $c = $('#c1').jcr() 
        .jcr('wings') 
        .jcr('selectOn', isTouchDevice() ? 'touchstart' : 'click');

    $.getJSON('images/images.json', function(images) {
        $.each(images, function(idx, img) {
            $c.jcr('add', '<li><img src="' + img.url + '"></li>');
        });
    });
});
<div id='c1' class='carousel'></div>
.carousel { 
    padding: 5px; 
    background-color: black; 
    border-radius: 5px;
} 

.carousel li { 
    margin: 0 2px; 
    border: 1px solid #ccc; 
    opacity: .7; 
    transition: opacity .5s; 
    -moz-transition: opacity .5s; 
    -webkit-transition: opacity .5s; 
} 

.carousel li:hover, .carousel li.selected { 
    opacity: 1; 
    border: 1px solid white; 
} 

.carousel li img {
    width: 120px; 
    height: 70px; 
}

Tell me more

Core

jcr has a small core, including the following initializer, methods and events:

Initializer
  • initializer ([options]): invoke by calling $obj.jcr({...}). Options include:
    • addDuration: the duration in ms to animate element addition
    • removeDuration: the duration in ms to animate element removal
    • msPer100px: the duration in ms to animate every 100 pixels, used to control sliding speed
    • showEasing: the name of the easing function used in show()
    • slideEasing: the name of the easing function used in slide()
    • You can set global defaults by overriding properties in $.fn.jcr.defaults
Methods
  • add (target [, position] [, callback])
    • target: the jQuery object or DOM element to be added
    • index (optional): the index to add to, default is the end of the list
    • callback (optional): the callback to be invoked after adding
  • remove (target [, callback])
    • target: the jQuery object, DOM element or index to be removed
    • callback (optional): the callback to be invoked after removing
  • show (target [, callback])
    • target: the jQuery object, DOM element, index, 'next', 'prev', 'previous' or 'nextN', 'prevN', 'previousN' (with N being the steps)
    • callback (optional): the callback to be invoked after showing
  • slide (to [, callback])
    • to: the position or offset (if prefixed with += or -=) to slide to
    • callback (optional): the callback to be invoked after sliding
  • canSlideLeft: return true if the carousel can slide further to the right.
  • canSlideRight: return true if the carousel can slide further to the right.
  • stop ([jumpToEnd]): immediately suspend any animation.
    • jumpToEnd (optional): jump to the end of the carousel when stopping, default is true
  • refresh ([callback]): recompute the carousel's relative position and dimensions. Should be invoked directly if there is any external change, e.g. add elements without using the add method.
Events
  • jcr.added (DOM element): triggered after an element is added and add animation is completed.
  • jcr.removed (DOM element): triggered after an element is removed and remove animation is completed.
  • jcr.shown (DOM element): triggered after an element is completely shown in view.
  • jcr.slided (position): triggered after the carousel has slided.
  • jcr.refreshed: triggered after the carousel has been refreshed.

You can write JavaScript code using these methos and events to control the behaviors of the carousel. You can also do it by coding plugins.

Plugins

You can code jcr plugins by adding methods to $.fn.jcr.plugins. jcr comes with 4 built-in plugins: wings, selectOn, swipe and hoverSlide. All these plugins are used in different examples in this page.

More examples

Flick

Flick and swipe guestures handling via the flick plugin.

$(function() {
    var $c = $('#c2').jcr()
        .jcr('flick');

    $.getJSON('images/images.json', function(images) {
        for (var i = 0; i < 50; i++) {
            var img = images[i % images.length];
            $c.jcr('add', '<li><img src="' + img.url + '"></li>');
        }
    });
});

Slide upon hover

Hover on the carousel to navigate to any image. This example doesn't go well with touch devices of course.

$(function() {
    var $c = $('#c3').jcr()
        .jcr('hoverSlide');

    $.getJSON('images/images.json', function(images) {
        $.each(images, function(idx, img) {
            $c.jcr('add', '<li><img src="' + img.url + '"></li>');
        });
    });
});

Animate add and remove

Refresh to see images are added with animation. Click or tap to remove. Images' width can be computed on the fly without causing any trouble.

$(function() {
    var $c = $('#c4').jcr({
        addDuration: 100,
        removeDuration: 300
    }).jcr('wings');

    $.getJSON('images/images.json', function(images) {
        (function add(index) {
            $c.jcr('add', '<li><img src="' + images[index].url + '" style="width:' +  rand(100, 200) + 'px;"></li>',

                // add() is async when addDuration is not 0
                // so using callback to simulate sequential addition
                function() {
                    if (++index < images.length) add(index); 
                    else {
                        $c.find('li').on(isTouchDevice() ? 'touchstart' : 'click', function() {
                            $c.jcr('remove', $(this));
                        });
                    }   
                }
            );
        })(0);
    });
});

Snap

Demonstrate a more advanced use of the wings plugin whereas images are snapped to the container's edges. Also notice the usage of showOnHover and style options. The carousel can also host any HTML contents, not just images.

$(function() {
    var $c = $('#c5').jcr().jcr('wings', {
        style: 'circle',
        showOnHover: false,
        snap: true                                    
    });

    $.getJSON('images/images.json', function(images) {
        $.each(images, function(idx, img) {
            $c.jcr('add', '<li><img src="' + img.url + '"><span class="title">' + img.name + '</span></li>');
        });
    });
});
<div id='c5'></div>
#c5 { 
    padding: 5px; 
    background-color: black; 
    border-radius: 5px;
    width: 500px;
} 

#c5 li { 
    margin: 0 2px; 
    border: 1px solid #ccc;
    width: 494px; 
    height: 300px;
    position: relative;
} 

#c5 li img {
    width: 100%; 
    height: 100%; 
}

#c5 .title {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;;
    background-color: black;
    color: white;
    text-align: center;
    opacity: 0.7;
}

Another example of the wings plugin with step set to 3.

$(function() {
    var $c = $('#c6').jcr().jcr('wings', {
        style: 'circle',
        showOnHover: false,
        snap: true,                               
        step: 3
    });

    $.getJSON('images/images.json', function(images) {
        $.each(images, function(idx, img) {
            $c.jcr('add', '<li><img src="' + img.url + '"><span class="title">' + img.name + '</span></li>');
        });
    });
});
<div id='c6'></div>
#c6 { 
    padding: 5px; 
    background-color: black; 
    border-radius: 5px;
    width: 750px;
} 

#c6 li { 
    margin: 0 2px; 
    border: 1px solid #ccc;
    width: 244px; 
    height: 150px;
    position: relative;
} 

#c6 li img {
    width: 100%; 
    height: 100%; 
}

#c6 .title {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;;
    background-color: black;
    color: white;
    text-align: center;
    opacity: 0.7;
}