Search

Automatic Infinite Carousel

Posted on 13th August 2009 — Following on from the infinite carousel, there have been a number of requests asking how to make the scrolling action automatic, so I’ve gone ahead to explain how to achieve this.

Watch

Watch jQuery Automatic Infinite Carousel screencast (Alternative flash version)

QuickTime version is approximately 8Mb, flash version is streaming.

View the demo used in the screencast

The Changes

To make the carousel automatically loop round by itself is pretty easy. In addition to automatic scrolling, I wanted to add the ability to make the scrolling pause when the mouse was over the carousel. To do this we need:

  1. An event we can trigger to go to the next panel
  2. A timer to keep firing the new custom event
  3. Mouseover and mouseout events to pause the timer

New Custom Event

As you’ll see from the original code we have the next and previous arrows. We could just trigger the next arrow click, but this feels a bit clunky.

So I’ve created a new event called “next” which is very similar to the next arrow being clicked:

$(this).bind('next', function () {
  gotoPage(currentPage + 1);
});

This code goes directly after our events inside the plugin, since it needs to access the currentPage variable and the gotoPage function.

We can test this custom event by calling the following in Firebug:

>>> $('.infiniteCarousel').trigger('next');

Next we need to make this call automatic.

Using Timers

There are two types of time:

  • setTimeout - runs a function in n milliseconds once
  • setInterval - runs a function every n milliseconds

We’ll use setInterval to trigger the next call every 2 seconds. Inside the $(document).ready() function we add the following after we attach the plugin:

setInterval(function () {
  $('.infiniteCarousel').trigger('next');
}, 2000);

Now the scrolling is automatic. Finally we need to only run this if the mouse isn’t over the infiniteCarousel element.

Using Mouse Events

We’ll create a flag variable to track whether we should be automatically scrolling or not. When the mouse goes over the infiniteCarousel, it will be false otherwise true. Then within the setInterval, we’ll only trigger the next event if we’re supposed to be scrolling:

var autoscrolling = true;

$('.infiniteCarousel').infiniteCarousel().mouseover(function () {
  autoscrolling = false;
}).mouseout(function () {
  autoscrolling = true;
});

setInterval(function () {
  if (autoscrolling) {
    $('.infiniteCarousel').trigger('next');
  }
}, 2000);

And that’s it. All we need to change to make the infinite carousel to run automatically.

Related posts

Demo

If you find this demo doesn't work as expected, it's possibly due to the demo running from within an iframe. Try running the demo in it's own window.

Source Code

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>Automatic Infinite Carousel</title>
<style type="text/css" media="screen">
<!--
body { font: 1em "Trebuchet MS", verdana, arial, sans-serif; font-size: 100%; }
input, textarea { font-family: Arial; font-size: 125%; padding: 7px; }
label { display: block; } 

.infiniteCarousel {
  width: 395px;
  position: relative;
}

.infiniteCarousel .wrapper {
  width: 315px; /* .infiniteCarousel width - (.wrapper margin-left + .wrapper margin-right) */
  overflow: auto;
  height: 105px;
  margin: 0 40px;
  position: absolute;
  top: 0;
}

.infiniteCarousel ul a img {
  border: 5px solid #000;
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
}

.infiniteCarousel .wrapper ul {
  width: 840px; /* single item * n */
  list-style-image:none;
  list-style-position:outside;
  list-style-type:none;
  margin:0;
  padding:0;
  position: absolute;
  top: 0;
}

.infiniteCarousel ul li {
  display:block;
  float:left;
  padding: 10px;
  height: 85px;
  width: 85px;
}

.infiniteCarousel ul li img {
    -webkit-transition: border-color 400ms;
}

.infiniteCarousel ul:hover li img {
  border-color: #000;
}

.infiniteCarousel ul:hover li:hover img {
  border-color: #333;
}

.infiniteCarousel ul li a img {
  display:block;
}

.infiniteCarousel .arrow {
  display: block;
  height: 36px;
  width: 37px;
  background: url(images/arrow.png) no-repeat 0 0;
  text-indent: -999px;
  position: absolute;
  top: 37px;
  cursor: pointer;
  outline: 0;
}

.infiniteCarousel .forward {
  background-position: 0 0;
  right: 0;
}

.infiniteCarousel .back {
  background-position: 0 -72px;
  left: 0;
}

.infiniteCarousel .forward:hover {
  background-position: 0 -36px;
}

.infiniteCarousel .back:hover {
  background-position: 0 -108px;
}



-->
</style>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">

(function () {
    $.fn.infiniteCarousel = function () {
        function repeat(str, n) {
            return new Array( n + 1 ).join(str);
        }
        
        return this.each(function () {
            // magic!
            var $wrapper = $('> div', this).css('overflow', 'hidden'),
                $slider = $wrapper.find('> ul').width(9999),
                $items = $slider.find('> li'),
                $single = $items.filter(':first')
                
                singleWidth = $single.outerWidth(),
                visible = Math.ceil($wrapper.innerWidth() / singleWidth),
                currentPage = 1,
                pages = Math.ceil($items.length / visible);
                
            /* TASKS */
            
            // 1. pad the pages with empty element if required
            if ($items.length % visible != 0) {
                // pad
                $slider.append(repeat('<li class="empty" />', visible - ($items.length % visible)));
                $items = $slider.find('> li');
            }
            
            // 2. create the carousel padding on left and right (cloned)
            $items.filter(':first').before($items.slice(-visible).clone().addClass('cloned'));
            $items.filter(':last').after($items.slice(0, visible).clone().addClass('cloned'));
            $items = $slider.find('> li');
            
            // 3. reset scroll
            $wrapper.scrollLeft(singleWidth * visible);
            
            // 4. paging function
            function gotoPage(page) {
                var dir = page < currentPage ? -1 : 1,
                    n = Math.abs(currentPage - page),
                    left = singleWidth * dir * visible * n;
                
                $wrapper.filter(':not(:animated)').animate({
                    scrollLeft : '+=' + left
                }, 500, function () {
                    // if page == last page - then reset position
                    if (page > pages) {
                        $wrapper.scrollLeft(singleWidth * visible);
                        page = 1;
                    } else if (page == 0) {
                        page = pages;
                        $wrapper.scrollLeft(singleWidth * visible * pages);
                    }
                    
                    currentPage = page;
                });
            }
            
            // 5. insert the back and forward link
            $wrapper.after('<a href="#" class="arrow back">&lt;</a><a href="#" class="arrow forward">&gt;</a>');
            
            // 6. bind the back and forward links
            $('a.back', this).click(function () {
                gotoPage(currentPage - 1);
                return false;
            });
            
            $('a.forward', this).click(function () {
                gotoPage(currentPage + 1);
                return false;
            });
            
            $(this).bind('goto', function (event, page) {
                gotoPage(page);
            });
            
            // THIS IS NEW CODE FOR THE AUTOMATIC INFINITE CAROUSEL
            $(this).bind('next', function () {
                gotoPage(currentPage + 1);
            });
        });
    };
})(jQuery);

$(document).ready(function () {
    // THIS IS NEW CODE FOR THE AUTOMATIC INFINITE CAROUSEL
    var autoscrolling = true;
    
    $('.infiniteCarousel').infiniteCarousel().mouseover(function () {
        autoscrolling = false;
    }).mouseout(function () {
        autoscrolling = true;
    });
    
    setInterval(function () {
        if (autoscrolling) {
            $('.infiniteCarousel').trigger('next');
        }
    }, 2000);
});


</script>

</head>
<body>

    <h1>Automatic Infinite Carousel</h1>
    
    <div class="infiniteCarousel">
      <div class="wrapper">
        <ul>
          <li><a href="http://www.flickr.com/photos/remysharp/3047035327/" title="Tall Glow"><img src="http://farm4.static.flickr.com/3011/3047035327_ca12fb2397_s.jpg" height="75" width="75" alt="Tall Glow" /></a></li>
          <li><a href="http://www.flickr.com/photos/remysharp/3047872076/" title="Wet Cab"><img src="http://farm4.static.flickr.com/3184/3047872076_61a511a04b_s.jpg" height="75" width="75" alt="Wet Cab" /></a></li>
          <li><a href="http://www.flickr.com/photos/remysharp/3047871878/" title="Rockefella"><img src="http://farm4.static.flickr.com/3048/3047871878_84bfacbd35_s.jpg" height="75" width="75" alt="Rockefella" /></a></li>
          <li><a href="http://www.flickr.com/photos/remysharp/3047034929/" title="Chrysler Reflect"><img src="http://farm4.static.flickr.com/3220/3047034929_97eaf50ea3_s.jpg" height="75" width="75" alt="Chrysler Reflect" /></a></li>

          <li><a href="http://www.flickr.com/photos/remysharp/3047871624/" title="Chrysler Up"><img src="http://farm4.static.flickr.com/3164/3047871624_2cacca4684_s.jpg" height="75" width="75" alt="Chrysler Up" /></a></li>
          <li><a href="http://www.flickr.com/photos/remysharp/3047034661/" title="Time Square Awe"><img src="http://farm4.static.flickr.com/3212/3047034661_f96548965e_s.jpg" height="75" width="75" alt="Time Square Awe" /></a></li>
          <li><a href="http://www.flickr.com/photos/remysharp/3047034531/" title="Wonky Buildings"><img src="http://farm4.static.flickr.com/3022/3047034531_9c74359401_s.jpg" height="75" width="75" alt="Wonky Buildings" /></a></li>
          <li><a href="http://www.flickr.com/photos/remysharp/3047034451/" title="Leaves of Fall"><img src="http://farm4.static.flickr.com/3199/3047034451_121c93386f_s.jpg" height="75" width="75" alt="Leaves of Fall" /></a></li>
        </ul>        
      </div>
    </div>
</body>
</html>

Comments

  1. crazy lobster On 13th January 2010 at 14:01

    hi there, thanks a lot for this tut. it’s awesome! i just got one problem: if i click the forward button just a sec before the automatic change, the timer keeps running and slides to the next image just when the remaining sec is over. know what i mean? is there any way to set the “timer” to 0, after i clicked forward? cheers

  2. R On 19th January 2010 at 10:01

    Is it possible to make this so that it’s W3C compliant? When I plugged the code from the demo into http://validator.w3.org/#validatebyinput it comes up with 4 errors. I wasn’t sure how to fix the errors.

Leave your own comment
  • http://