1

I am creating a utility method which helps fading the elements sequentially with jQuery. As you can see in the below code I am adding an extra class as alreadyFadedIn a flag. At the end of the method call sequentiallyFadeIn(...) I would like to perform the cleanUp where I want to remove the flag class which I added in the selected elements inside the sequentiallyFadeIn(...) method.

<html>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
$(document).ready(function() {

    function sequentialFadeIn(selectorText, speed, display, callBack) {

        display = typeof display !== 'undefined' ? display : "block";

        function helper() {
            nextElementToFadeIn = $(selectorText).not(".alreadyFadedIn").first();
            nextElementToFadeIn.fadeIn(speed, function() {
                $(this).addClass("alreadyFadedIn");
                helper();
            }).css("display", display);
        }
        helper();

        callBack(selectorText);      
    }
    sequentialFadeIn(".toBeFaddedIn", "slow", "inline-block",function cleanUp(selectorText1){
            $(selectorText1).removeClass("alreadyFadedIn");
            console.log("Clean up has been performed.");
                    console.log("Selector Text: " +selectorText1);

        } );

});

</script>

</head>
<body><style media="screen" type="text/css">
.hello {
    background-color: blue;
    height:50px;
    width: 50px;
    display: none;

}
</style>

<div class="hello toBeFaddedIn"></div>
<div class="hello toBeFaddedIn"></div>
<div class="hello toBeFaddedIn"></div>
<div class="hello toBeFaddedIn"></div>
<div class="hello toBeFaddedIn"></div>

</body></html>

While looking at the inspect element I notice that class alreadyFadedIn is not getting removed. The cause seems to me is the cleanUp method gets executed asynchronously along with main logic of the sequentiallyFadeIn method which is in helper(). You can also notice the log message "Clean up has been performed." getting printed even before the divs have completed fading in.

How can I make the cleanUp call getting executed after the completion of main logic in the sequentiallyFadedIn method? Any suggestions?

Code on jsfiddle: http://jsfiddle.net/BztLx/11/

Atharva
  • 6,711
  • 5
  • 31
  • 39
  • 1
    Looks like you have to run the callback inside the `helper` function once `nextElementToFadeIn` does not contain any element anymore. – Felix Kling Nov 24 '12 at 17:22
  • You need to call the `callBack` function in the completion function of `fadeIn`. – Barmar Nov 24 '12 at 17:22
  • Your code executes `helper` (which removes the class `alreadyFadedIn`) immediately, and the anonymous function which adds the class `alreadyFadedIn` is executed later, when the fadeIn animation is complete. That seems wrong. But you have the ingredients there to fix the logic. You can execute code immediately or on animation end. – Jim Blackler Nov 24 '12 at 17:24
  • This looks overly complicated. Why not chain fade-in using next(), without the need for adding a class? – Christophe Nov 24 '12 at 17:25
  • @FelixKling your solution worked ! : ) Here is the updated code on jsfiddle: http://jsfiddle.net/BztLx/14/ – Atharva Nov 24 '12 at 17:31
  • @Christophe I would love to see your simpler version! Can you post a solution and comment back here a demo link please? – Atharva Nov 24 '12 at 17:34
  • @Atharva sure, I have added an example. – Christophe Nov 24 '12 at 18:12

3 Answers3

4

You need to check whether any elements remain to be faded in. If no elements remain, call the cleanup callback. Here is how I implemented it:

    if ($(selectorText).is(":not(.alreadyFadedIn)")) {

        //Your main logic
        nextElementToFadeIn = $(selectorText).not(".alreadyFadedIn").first();
        nextElementToFadeIn.fadeIn(speed, function() {
            $(this).addClass("alreadyFadedIn");
            helper();
        }).css("display", display);

    } else {

        //No elements remain, cleanup time
        callBack(selectorText);
    }

In the outer condition I check whether there is at least one element that is not faded in, otherwise I invoke the callback.

Demonstration: http://jsfiddle.net/BztLx/12/

Asad Saeeduddin
  • 46,193
  • 6
  • 90
  • 139
  • I also came up with something similar based on @FelixKing's suggestion in the comments under the question. Demo: http://jsfiddle.net/BztLx/14/ – Atharva Nov 24 '12 at 17:33
3

Easier, cleaner and faster if you just rewrite your code something like this...

$(document).ready(function() {

    function sequentialFadeIn(selectorText, speed, display, callBack) {

        display = display || "block";

        var els = $(selectorText),
              i = 0;

        (function helper() {
            els.eq(i++).fadeIn(speed, helper).css("display", display);
            if (callback && i === els.length)
                callback(selectorText); // Not really needed any more
        })();
    }

    sequentialFadeIn(".toBeFaddedIn", "slow", "inline-block", function cleanUp(selectorText1){
        // not really needed any more
       //  $(selectorText1).removeClass("alreadyFadedIn");
    });
});

DEMO: http://jsfiddle.net/BztLx/15/

You were doing way more DOM selection than needed.

I Hate Lazy
  • 47,415
  • 13
  • 86
  • 77
  • 1
    Aah ! Beautiful : ) When we have the `.eq()` as looper, who needs the extra flags! Thanks for pointing out this way. – Atharva Nov 24 '12 at 18:06
2

To expand on my comment, here is a simplified version that doesn't use an additional class:

function fadeThenNext(element){
    element.fadeIn("fast", function() {
        element=element.next(".toBeFaddedIn");
        if (element.length) fadeThenNext(element);
    });
}
fadeThenNext($(".toBeFaddedIn").first());

demo: http://jsfiddle.net/BztLx/17/

[Update] A more generic version if the elements are not siblings:

function fadeSequence(elements){
    elements.first().fadeIn("fast", function() {
        fadeSequence(elements.slice(1));
    });
}
fadeSequence($(".toBeFaddedIn"));

​fiddle: http://jsfiddle.net/BztLx/22/

Christophe
  • 27,383
  • 28
  • 97
  • 140
  • It requires the elements selected to be siblings of each other, it may not be the case sometimes. But applicable to some scenarios where this effect needs to be applied say on a list of elements. – Atharva Nov 24 '12 at 18:47
  • right, for a more generic code you'll need to iterate through the selector as in @user1689607 's sample. The point is that you don't need the extra class! – Christophe Nov 24 '12 at 18:56