Search code examples
javascriptjqueryjquery-deferreddeferred

One Div animation after another using deferred object


Animate the Div after the previous Div animation has completed using deferred object. This simple method works with the two functions f1 and f2, however when I introduce f3 it fails.

Is there a better way I can achieve this using the deferred object?

JSFiddle: https://jsfiddle.net/j0bgzjvd/

var deferred = $.Deferred();

function animationAgent(element, prevElement) {
  $(prevElement).promise().done( function () {
    return $(element).css("display", "block").animate({width:360},2000, "linear")
  });
}

function f1() {
  animationAgent("#div1"); 
} 

function f2() {
  animationAgent("#div2", "#div1"); 
} 

function f3() {
  animationAgent("#div3", "#div2"); 
}

deferred.resolve();
deferred.done( [ f1, f2, f3 ] );
div {
  width: 200px; 
  height: 200px; 
  background-color: red; 
  margin-bottom: 10px; 
  display: none;  
}
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>

<div id="div1"></div>
<div id="div2"></div>
<div id="div3"></div>


Solution

  • You will find it simpler to :

    • make animationAgent() into a simple, promise-returning worker function that knows only about the element it animates, and nothing about the sequence in which it is to be used (ie omit prevElement),
    • arrange for functions f1(), f2() and f3(), to return the promise returned to them by animationAgent().

    Then you have the basis for building a reliable animation sequence.

    function animationAgent(element) {
        return $(element).css("display", "block").animate({width:360}, 2000, "linear").promise();
    }
    function f1() {
        return animationAgent("#div1"); 
    } 
    function f2() {
        return animationAgent("#div2"); 
    } 
    function f3() {
        return animationAgent("#div3"); 
    }
    
    f1().then(f2).then(f3);
    

    DEMO

    Alternatively, construct the .then chain mechanistically from an array of function references :

    function animationAgent(element) {
        return $(element).css("display", "block").animate({width:360}, 2000, "linear").promise();
    }
    function f1() {
        return animationAgent("#div1"); 
    } 
    function f2() {
        return animationAgent("#div2"); 
    } 
    function f3() {
        return animationAgent("#div3"); 
    }
    
    [f1, f2, f3].reduce(function(promise, fn) {
        return promise.then(function() {
            return fn();
        });
    }, $.when());
    

    DEMO

    Or, as the three animations are identical, you can avoid the need for individual functions by constructing the .then chain from an array of element selectors and calling animationAgent() directly :

    function animationAgent(element) {
        return $(element).css("display", "block").animate({width:360}, 2000, "linear").promise();
    }
    
    ['#div1', '#div2', '#div3'].reduce(function(promise, selector) {
        return promise.then(function() {
            return animationAgent(selector);
        });
    }, $.when());
    

    DEMO