Build your own carousel

mmarcon | 29 March, 2012 09:00

Nowadays, whatever effect or component a developer may need for a website or web application is available in the form of a jQuery plugin. Every other day I see a blog post with a title like "The best 25 jQuery carousels" or "The best 50 photo gallery plugins for jQuery". Alright, I get it, one should not reinvent the wheel: if somebody builds a plugin that suits your needs it makes sense to use it rather than spending time reimplementing the whole thing from scratch by yourself. Unfortunately too many times developers are willing to adapt their markup or even their layout to accommodate the requirements of the plugin they use. Often plugins expose several features and support endless options for customization: while this may result very convenient, the price is a quite large amount of code that in most cases is unnecessary. This translates to more bytes (or kilobytes) delivered to the user's browser and possibly an increased perceived loading time for the web page containing the widget.

In situations where keeping the codebase small and efficient is crucial, it is probably worth to invest a little time and write a completely customized component from scratch, strictly based on the needs. Eventually with a few bytes more the widget can be converted to a simple plugin in case it has to be used in other pages or application components. In the last few weeks at City Pages we have been working on a new feature that required a carousel to nicely display some information to the user. After a quick analysis of the problem we chose to implement our own little carousel instead of relying on any of the thousands of plugins out there: this way we could quickly come up with a completely customized carousel component with only a few lines of JavaScript and some small changes to our CSS file. In this post I am going to explain how to build a carousel using jQuery and some CSS tricks, following the steps we took when developing the one for City Pages.

The idea

Here is what the final carousel will look like.

Carousel Screenshot  

The carousel will slide horizontally with a nice sliding animation and will be a circular carousel in the sense that after the last item the first one is shown. In order to obtain this circular effect the carousel items will be arranged by the script as follows:

Carousel Diagram 

The last item is cloned and placed before all the other items and the first item is cloned and appended at the end. When the viewport is showing number 1, sliding backwards animates to the cloned version of number 3 and then seamlessly changes the viewport to the actual number 3. The next sections describe in details the markup and CSS for the carousel and the JavaScript code that creates the interaction.

The markup

First of all we need some markup. It can be either generated on the server side - good for SEO purpose - or dynamically created front-end side, but here is what it should look like:


<!-- A container for the carousel -->
<div class="carousel-wrapper">    <!-- The actual carousel -->
<ul class="carousel">
    <li class="item">
        <img src="some/image/here.png" alt=""/>
        <p class="text"><!-- Item text here --></p>
    </li>
    <li class="item">
        <img src="some/image/here.png
        <p class="text"><!-- Item text here --></p>
    </li>
    <li class="item">
        <img src="some/image/here.png
        <p class="text"><!-- Item text here --></p>
    </li>
    <li class="item">
        <img src="some/image/here.png
        <p class="text"><!-- Item text here --></p>
    </li>
</ul>
<!-- The prev/next controls -->
<div class="carousel-nav">
    <a href="" class="prev">&lt;</a>
    <a href="" class="next">&gt;</a>  
</div>
</div>

There is not much to say here: we have a wrapper block that contains the carousel itself and the navigation controls. The carousel is a ul element and the carousel items as expected are li elements in a perfectly semantic and SEO friendly fashion.

The CSS

As shown in the diagram above, the idea is to place the items horizontally, and set the viewport size so only one of them at the time is visible. In our case the viewport is the wrapper element, and what we want to obtain is a wide ul that slides left and right within the wrapper to show the active item and hide all the others. The images will always represent the background of the carousel items and the text will be displayed on the right. We also want the carousel controls to be always shown on the left and on the right of the wrapper horizontally centered. Additionally we will add some rules to make it beautiful.

In order to accomplish that we'll add the following CSS rules:


.carousel-wrapper {    position: relative; /*Reference for absolutely positioned descendants*/
    width: 800px; /*Actual width each item should have*/
    height: 240px; /*Actual height each item should have*/
    overflow: hidden; /*So only one item is visible*/
    border-top: 1px dotted #ccc;
    border-bottom: 1px dotted #ccc;
    margin: 20px 0;
}
.carousel {
    position: absolute; /*This way we can dynamically "move" the ul from the JavaScript by changing the left*/
}
.carousel li {
    display: block;
    float: left; /*Place items horizontally*/
    width: 800px;
    height: 240px;
    position: relative; /*Reset the reference to absolutely position children*/
}
.carousel img {
    position: absolute; /*Goes top-left (note that we work under the assumption that the image is the right size)*/
    z-index: 0; /*Goes below everything*/
}
.carousel p.text {
    position: absolute;
    z-index: 1;
    font-size: 24px;
    width: 340px;
    right: 80px;
    top: 10px;
    color: #ff6347;
    background-color: #111111; /*IE tolerant*/
    background-color: rgba(0,0,0,0.7);
    padding: 5px;
}
.carousel-nav a {
    display: block;
    height: 40px;
    width: 30px;
    font-size: 24px;
    line-height: 1.6;
    font-weight: 800;
    color: #222222;
    background-color: rgb(255,99,71); /*IE tolerant*/
    background-color: rgba(255,99,71,0.7);
    text-decoration: none;
    padding: 0 4px;
    position: absolute;
    top: 50%;
    margin-top: -20px;
    text-align: center;
    z-index: 10; /*Always on top*/
}
.carousel-nav a.prev {
    left: 0; /*Prev goes on the left*/
    -webkit-border-top-right-radius: 4px; /*Some rounded corners fanciness*/
    -webkit-border-bottom-right-radius: 4px;
    -moz-border-radius-topright: 4px;
    -moz-border-radius-bottomright: 4px;
    border-top-right-radius: 4px;
    border-bottom-right-radius: 4px;
}
.carousel-nav a.next {
    right: 0; /*Next goes on the right*/
    -webkit-border-top-left-radius: 4px; /*Some rounded corners fanciness*/
    -webkit-border-bottom-left-radius: 4px;
    -moz-border-radius-topleft: 4px;
    -moz-border-radius-bottomleft: 4px;
    border-top-left-radius: 4px;
    border-bottom-left-radius: 4px;
}

The JavaScript

We will use jQuery for simplicity. The following code registers event handlers for the carousel controls, handles sliding animation and repositions the ul element when necessary.


//We don't want to poison the global scope
//therefore we wrap everything into an anonymous self-executing function
(function() {
    var first = $('.item').first(),
        last = $('.item').last(),
        itemWidth = first.width(),
        carousel = $('.carousel');
    //Adds the clones of the first and last items, as shown in reddish in the picture above
    carousel.prepend(last.clone()).append(first.clone());
    //Determine the width the carousel ul should have
    carousel.width(itemWidth * $('.item').length);
    //Scroll to the right position to show the first element
    carousel.css({left: -itemWidth});
    //Setup handlers...
    $('.prev').on('click', function(e){
        e.preventDefault();
        //Some nice sliding animation
        carousel.animate({left: '+=' + itemWidth}, 300, function(){
            if(Math.abs(carousel.position().left) < 2) { //See Note below
                //Handles circular carousel when we get to the left as much as possible
                carousel.css({left: -itemWidth * (carousel.children().length - 2)});
            }
        });
        return false;      
    });
    $('.next').on('click', function(e){
        e.preventDefault();
        carousel.animate({left: '-=' + itemWidth}, 300, function(){
            if(Math.abs(carousel.position().left + itemWidth * (carousel.children().length - 1)) < 2) { //See Note below
                //Handles circular carousel when we get to the right as much as possible
                carousel.css({left: -itemWidth});
            }
        });
        return false;      
    });
})();

Note: instead of comparing the current position with 0 in the previous case and to itemWidth * (carousel.children().length - 1) in the next case we compare the absolute value and the absolute value of the difference with 2 (2px) respectively. The reason for this is that we noticed older versions of IE do some sort of approximation and the value was never 0 as one would expect. This way the code is more tolerant to this sort of browser weirdnesses.

And finally the working demo and all the code. The page was tested with Chrome, Firefox 9+, Safari, IE8 and IE9.

Update: we just released an updated version of City Pages, where you can see the carousel in action for displaying City Facts.

Nokia Developer aims to help you create apps and publish them so you can connect with users around the world.

京ICP备05048969号  © Copyright Nokia 2013 All rights reserved