Jquery fadeOut/fadeIn callback not working
Asked Answered
S

2

16

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?

Shantelleshantha answered 17/4, 2012 at 0:39 Comment(7)
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. jquery.malsup.com/cycleKarney
The variable listsize is not defined, do you mean listlength?Exterritorial
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. ;)Shantelleshantha
@Exterritorial i've simplify my code for this post and forgot this variable, but it's not the issue's cause.Shantelleshantha
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?Inflatable
@Inflatable Here is what happen: jsfiddle.net/67Wu7/46Shantelleshantha
@Shantelleshantha That is because your callback function is called for every item that is faded in.Exterritorial
I
28

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.

Inflatable answered 17/4, 2012 at 1:32 Comment(2)
This is it, thanks a lot, i've understand a bit more about jquery ;)Shantelleshantha
Info on Promise 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.Claiborn
L
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);
  });

});
Lowther answered 17/4, 2012 at 1:18 Comment(4)
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 !Shantelleshantha
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 ;)Lowther
+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.Inflatable
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. ;)Lowther

© 2022 - 2024 — McMap. All rights reserved.