14

i m building a little script to animate a list. Here is my html structure:

<ul>
   <li class="slider"> Item-1 </li>
   <li class="slider"> Item-2 </li>
   <li class="slider"> Item-3 </li>
   ...
   <li class="slider"> Item-13 </li>
   <li class="slider"> Item-14 </li>
   <li class="slider"> Item-15 </li>
</ul>

<button> Next </button>

I'm displaying only four li at one time, the "next" button fadeOut the displayed four li et fadeIn the next four ones. But the fades are applying both together. I've tried to use callback function on the first fade but i can't make it work.

here is the script:

$('li:gt(3)').css('display', 'none');

//Define the interval of li to display
var start = 0;
var end = 4;

//Get the ul length
var listlength = $("li").length;

$("button").click(function() { 

  // FadeOut the four displayed li 
  $('ul li').slice(start,end).fadeOut(500, function(){

        // Define the next interval of four li to show
        start = start+4;
        end = end+4;

        // Test to detect the end of list and reset next interval
        if( start > listlength ){
          start = 0;
          end = 4;
        }

        //Display the new interval
        $('ul li').slice(start,end).fadeIn(500);
  });    
});

Any clues?

Joeyjoejoe
  • 686
  • 2
  • 6
  • 18
  • Second time today I get to recommend the awesome jQuery.cycle plugin. It'll make your life a **lot** easier. It's designed for slideshows but fits your use case perfectly. http://jquery.malsup.com/cycle/ – Endophage Apr 17 '12 at 00:41
  • The variable listsize is not defined, do you mean listlength? – dennisg Apr 17 '12 at 00:45
  • Thanks a lot, it looks very good! But i like to understand things, so if i can't make a simple smooth fadeIn/Out on my own, i prefer learning before using nice plugin like this. ;) – Joeyjoejoe Apr 17 '12 at 00:51
  • @dennisg i've simplify my code for this post and forgot this variable, but it's not the issue's cause. – Joeyjoejoe Apr 17 '12 at 00:54
  • What does "can't make [the callback] work" mean? Do you get an error in the console? Does the wrong thing happen? Does _anything_ happen? – nnnnnn Apr 17 '12 at 01:08
  • @nnnnnn Here is what happen: http://jsfiddle.net/67Wu7/46/ – Joeyjoejoe Apr 17 '12 at 01:19
  • 1
    @Joeyjoejoe That is because your callback function is called for every item that is faded in. – dennisg Apr 17 '12 at 01:24

2 Answers2

27

The problem is that the .fadeOut() callback is called once per animated element, not once at the end. You could modify your code to keep a counter of how many times it has been called, but far easier - assuming at least jQuery 1.6 - is to use .promise(), which will resolve after all the associated animations complete:

$(document).ready(function () {
    var $lis = $("li.slider"),
        start = 0;

    $lis.hide()
        .slice(start, start + 4)
        .show();

    $("button").click(function () {
        $lis.slice(start, start + 4)
            .fadeOut(500)
            .promise()
            .done(function () {
                start += 4;
                if (start > $lis.length) {
                    start = 0;
                }
                $lis.slice(start, start + 4).fadeIn();
            });
    });
});

Demo: http://jsfiddle.net/w7Yuk

I made a couple of other changes to your code, e.g., caching the jQuery object with the li elements, and removing the "end" variable.

Elias Zamaria
  • 80,938
  • 29
  • 103
  • 136
nnnnnn
  • 138,378
  • 23
  • 180
  • 229
  • 1
    This is it, thanks a lot, i've understand a bit more about jquery ;) – Joeyjoejoe Apr 17 '12 at 01:48
  • Info on Promise http://api.jquery.com/promise/ Description: Return a Promise object to observe when all actions of a certain type bound to the collection, queued or not, have finished. – nycynik Sep 10 '12 at 18:04
5

I created a nice little jsFiddle demo that modifies what you had and gets you a nice smooth transition:

HTML:

Give button an id of "next" so that you can target it specifically, in case there are other buttons on page.

<ul>
   <li class="slider"> Item-1 </li>
   <li class="slider"> Item-2 </li>
   <li class="slider"> Item-3 </li>
   <li class="slider"> Item-4 </li>
   <li class="slider"> Item-5 </li>
   <li class="slider"> Item-6 </li>
   <li class="slider"> Item-7 </li>
   <li class="slider"> Item-8 </li>
   <li class="slider"> Item-9 </li>
   <li class="slider"> Item-10 </li>
   <li class="slider"> Item-11 </li>
   <li class="slider"> Item-12 </li>
   <li class="slider"> Item-13 </li>
   <li class="slider"> Item-14 </li>
   <li class="slider"> Item-15 </li>
   <li class="slider"> Item-16 </li>
</ul>

<button id="next"> Next </button>

CSS:

Start both off with display none so we can fade them in nicely on load.

.slider { display: none; }
#next { display: none; }

jQuery:

I like to cache elements, so I started off by doing that. I then fade in both the first 4 LI elements and the next button. I use the recommended handler of .on() to bind the click event of the next button. After we set start and end we call .fadeOut() on the next button and the current 4 LI elements. Now, the reason your callback is screwy is due to the fact that their is a callback for every element in your selector ( so 4 times ). Instead, we need to use .promise() to wait for all of them to complete as a whole and then we can call the .fadeIn() method on both the next button and the next 4 LI elements. Just a side note, I use .stop(true,true) to eliminate any animation queuing that there might be.

var $list = $("ul li");
var $next = $("#next");
var start = 0;
var end = 4;

$next.fadeIn(500);
$list.slice(start,end).fadeIn(500);

$next.on("click", function() {

  start += 4;
  end += 4;

  if( start >= $list.length ){
    start = 0;
    end = 4;
  }

  $next.stop(true,true).fadeOut(500);
  $list.stop(true,true).fadeOut(500);

  $list.promise().done(function() {
    $list.slice(start,end).stop(true,true).fadeIn(500);
    $next.stop(true,true).fadeIn(500);
  });

});
Code Maverick
  • 19,231
  • 10
  • 57
  • 111
  • Thanks a lot, i'll got an eye on your script later, it looks better than mine. I ve accepted other answer, because it was closest to my code, but you're answer is really nice and well commented. Thank you ! – Joeyjoejoe Apr 17 '12 at 01:51
  • No problem. ;) Just a note, mine was up a good 10+ min before his. I'm not implying he took my answer, but it is awful close. That aside, I do think you'll find this solution behaves a lot smoother in the UI. Give it a go! Happy Coding ;) – Code Maverick Apr 17 '12 at 02:07
  • +1. Yes you answered first, though I didn't copy (or even see) your answer until after I posted mine - at the time you posted I was struggling to type up my solution on my smart phone, so that's what the delay was... Any similarity is due to .promise() being the best solution. – nnnnnn Apr 17 '12 at 02:38
  • No worries. Like I said, I wasn't trying to imply that, it just struck me as an odd coincidence. +1 to you as well for attacking this on a smart phone. ;) – Code Maverick Apr 17 '12 at 11:15