Responsive thumbnail carousel

I have a jsfiddle here - http://jsfiddle.net/ZrHfu/1/

Demo here - http://www.ttmt.org.uk/forum/thumb_test/

I’m trying to create a responsive thumbnail carousel - I’ve seen plugins to do this but they all need images the same size, I need something with images in different sizes.

It’s very basic - list of images floated left, overflow: hidden on container.

I have left/right buttons, when the button is clicked I’m trying to move the ul with the images left/right.

Only the right button is connected.

My problem is it only works once.

If you click the right button, the images move left, click it a second time and nothing happens.

Why does it work only once.


<!DOCTYPE html>
    <html>
      <meta charset="UTF-8">
      <title>Title of the document</title>
      <meta name="description" content="">
      <meta name="keywords" content="">
      <meta name="robots" content="">

      <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />

      <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>

      <link rel="stylesheet" href="css/master.css" />

      </head>

    <body>


        <a class="arrow left">&larr;</a>

        <div class="thumbs">

          <ul>

            <li><a href="#"><img alt="" src="images/01.jpg" /></a></li>
            <li><a href="#"><img alt="" src="images/02.jpg" /></a></li>
            <li><a href="#"><img alt="" src="images/03.jpg" /></a></li>
            <li><a href="#"><img alt="" src="images/04.jpg" /></a></li>
            <li><a href="#"><img alt="" src="images/05.jpg" /></a></li>
            <li><a href="#"><img alt="" src="images/06.jpg" /></a></li>
            <li><a href="#"><img alt="" src="images/07.jpg" /></a></li>
            <li><a href="#"><img alt="" src="images/08.jpg" /></a></li>
            <li><a href="#"><img alt="" src="images/09.jpg" /></a></li>
            <li><a href="#"><img alt="" src="images/10.jpg" /></a></li>
            <li><a href="#"><img alt="" src="images/11.jpg" /></a></li>
            <li><a href="#"><img alt="" src="images/12.jpg" /></a></li>
            <li><a href="#"><img alt="" src="images/13.jpg" /></a></li>
            <li><a href="#"><img alt="" src="images/14.jpg" /></a></li>
            <li><a href="#"><img alt="" src="images/15.jpg" /></a></li>

          </ul>
        </div>

        <a class="arrow right">&rarr;</a>


        <script src="js/hel.js"></script>

    </body>

    </html>


Hi,

First off, thank you for asking your question in such a clear and concise way. I wish more people would do this.

The problem is this line:

$('.thumbs ul').animate({'left': -width } , 1000);

If width is 400px, you are animating the <ul> tag inside the “thumbs” <div> to a position of left: -400px.
Once it has reached this position, there is nowhere else for it to go.

What you want to do instead is this:

$('.thumbs ul').animate({'left': '-=' + width } , 1000);

Whereby every time you click the arrow, you are subtracting width from the <ul> element’s current left position and animating to that value.

For example, if you start off at left: 0px and width is 400px:

Click 1: 0 - 400 = -400, animate <ul> from left: 0px to left: -400px
Click 2: -400 - 400 = -800, animate <ul> from left: -400px to left: -800px
Click 3: -800 - 400 = -1200, animate <ul> from left: -800px to left: -1200px

and so on …

Thanks Pullo, I understand now.

My question was clear because I have to asked a lot of questions, and here’s another.

How do I stop the ul moving when the last image is in view.

I knew i would have to address this but I thought I might be able to use the
overall width of the ul with the width of the containing div.

The ul’s width is set very wide to accommodate the floated img’s so I don’t think this would work.

Could I use a conditional statement that ran the animate function if the last image had NOT past the right button?

It’s not so hard, what you need to do is this:

  • Get references to the width of the thumbs container and to the width of the <ul> element containing the thumbs.
  • Get a reference to the left offset of the <ul> containing the thumbs.
  • When the user presses scroll left, check that the left offset of the <ul> + the width of the thumbs container are less than, or equal to the total width of the <ul>. If so, then you have enough space for another scroll, if not, then just scroll by the remaining space.
  • When scrolling right, just don’t go beyond offset left of zero

Does that make sense?

That is how I thought I would do it but the width of the <ul> containing the thumbs is wider than the actual width of the of all the thumbs together.

I have set the width of the <ul> to 4000px to accommodate the thumbs.

I thought I could do something like:



.thumbs ul{
    	width:auto;
    	position:relative;
}


so the <ul> would be as wide as it’s contents but that doesn’t seem to work.

I’ve had to make the <ul> wider than it’s contents.

Here is the code with the CSS


<!DOCTYPE html>
<html>
  <meta charset="UTF-8">
  <title>Title of the document</title>
  <meta name="description" content="">
  <meta name="keywords" content="">
  <meta name="robots" content="">
  
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
  
  <style type="text/css" >
    *{
      margin:0;
      padding:0;
    }
    body{
    	background:#eee;
    }
    .thumbs{
    	width:80%;
    	overflow:hidden;
    	margin:100px;
    }
    .thumbs ul{
    	width:4000px;
    	position:relative;
    }
    .thumbs ul li{
    	display:inline;
    }
    .thumbs ul li a{
    	display:inline-block;
    	float:left;
    }
    .arrow{
    	font-size:2em;
    	display:inline-block;
    	padding:5px;
    	background:red;
    	position:absolute;
    }
    .left{
    	top:100px;
    	left:10px;
    }
    .right{
    	right:5%;
    	top:100px;
    }
  </style>
    
  </head>
  
<body>
  
    
    <a class="arrow left">&larr;</a>
    
    <div class="thumbs">

      <ul>

        <li><a href="#"><img alt="" src="images/01.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/02.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/03.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/04.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/05.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/06.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/07.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/08.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/09.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/10.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/11.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/12.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/13.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/14.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/15.jpg" /></a></li>
        
      </ul>
    </div>
  
    <a class="arrow right">&rarr;</a>
    
    
    <script src="js/hel.js"></script>
    
</body>

</html>


Hi there,

So I changed the CSS so that your <ul> doesn’t need an explicit length declaration, then implemented the rest for you.
I’ve also added scrolling to the “.left” button, so that the whole thing scrolls both ways.
If there’s anything you don’t understand, just let me know.

Demo

Code:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Responsive slider demo</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
    <link rel="stylesheet" href="css/reset.css" />
    <style>
      body{ background:#eee; }
      .thumbs{
        width:80%;
        overflow:hidden;
        margin:100px;
        white-space: nowrap;
      }
      .thumbs ul{
        position:relative;
        float:left;
      }
      .thumbs ul li{
        display: inline-block;
      }
      .arrow{
        font-size:2em;
        display:inline-block;
        padding:5px;
        background:red;
        position:absolute;
      }
      .left{
        top:100px;
        left:10px;
      }
      .right{
        right:5%;
        top:100px;
      }
    </style>
  </head>
  
  <body>
    <a class="arrow left">&larr;</a>
    <div class="thumbs">
      <ul>
        <li><a href="#"><img alt="" src="images/01.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/02.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/03.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/04.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/05.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/06.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/07.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/08.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/09.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/10.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/11.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/12.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/13.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/14.jpg" /></a></li>
        <li><a href="#"><img alt="" src="images/15.jpg" /></a></li>
      </ul>
    </div>
    <a class="arrow right">&rarr;</a>
    
    <script>
      $(window).load(function(){
        var $thumbsContainer = $(".thumbs ul"),
            thumbsDisplayWidth = $(".thumbs").width(),
            totalThumbsWidth = $thumbsContainer.width(),
            currentPosition = 0,
            scrollAmount;
        
        $('.right').click(function(){
          if(currentPosition < totalThumbsWidth - (2 * thumbsDisplayWidth)){
            scrollAmount = thumbsDisplayWidth;
          } else {
            scrollAmount = totalThumbsWidth - (thumbsDisplayWidth + currentPosition)
          }
          $thumbsContainer.filter(':not(:animated)').animate({'left': '-=' + scrollAmount }, 1000, function(){
            currentPosition = Math.abs(parseInt($thumbsContainer.css('left')));
          });
        })
        
        $('.left').click(function(){
          if(currentPosition > 0 && currentPosition > thumbsDisplayWidth){
	    scrollAmount = thumbsDisplayWidth;
	  } else {
	    scrollAmount = totalThumbsWidth - (totalThumbsWidth - currentPosition);
	  }
          $thumbsContainer.filter(':not(:animated)').animate({'left': '+=' + scrollAmount }, 1000, function(){
            currentPosition = Math.abs(parseInt($thumbsContainer.css('left')));
          });
        });
      })
    </script>
  </body>
</html>

wow thats brilliant thanks.

Only bit I’m not to sure about is


$thumbsContainer.filter(':not(:animated)').animate({'left': '-=' + scrollAmount }, 1000, function(){
            currentPosition = Math.abs(parseInt($thumbsContainer.css('left')));
          });

Ok, let’s break it down:

You probably know about the problem with animations stacking up and that if someone kept hammering on the right or left buttons of your scroller, then it would scroll way past the position it was meant to.
Well, this bit here, says to filter the selection, only to include elements that are not being animated. That way we avoid the problem altogether:

.filter(':not(:animated)')

Then we pass an anonymous callback to animate(), so that we can update the scroller’s current position, once the animation has completed.

The current position is obtained, thus:

currentPosition = Math.abs(parseInt($thumbsContainer.css('left')));

Working from the inside out:

$thumbsContainer.css('left')

will give us a pixel value between 0 and minus the width of the thumbs container. This will be a string.

parseInt($thumbsContainer.css('left'))

We apply parseint() to this value, as it will parse a string (ignoring the ‘px’) and returns an integer value.

Math.abs(parseInt($thumbsContainer.css('left')));

We then use Math.abs() to turn a potential negative number into a positive one.

And that’s it!

Does that all make sense?

You might also want to read: http://css-tricks.com/full-jquery-animations/

Perfect, thanks for all your help