jQuery animate() callback remembers only last variable in a loop

141 Views Asked by At

I am writing a script which basically makes a box travel every time to different places and it does a specified numbers of loops. The movement of the box is just an animate() function, which is in a while loop.

In this loop we also randomly select the coordinates to which the should go and comeback from. After it goes to the specified coordinates I insert a div to this exact location and make it red-colored. This I do in the callback function.

The problem that occurs is that a div is only added to the last coordinates which the box goes to.

var i =0;

while( i<5 ){ //specifying the cycles for the box' movement

var hstandWidth = $("#hstand").width()+27; //getting the current x-coordinates

//array to randomly select x-coordinates from 
var items = Array(hstandWidth-(50)*1, hstandWidth-(50)*2, hstandWidth-(50)*3, hstandWidth-(50)*4, hstandWidth-(50)*5,hstandWidth-(50)*6,hstandWidth-(50)*7,hstandWidth-(50)*8);                                         
//variable with the x-coordinates, which are randomly fetched from the array
var moveLeft = items[Math.floor(Math.random()*items.length)];

//array to randomly select y-coordinates from 
var items2 = Array(30,30*2,30*3,30*4,30*5,30*6,30*7);
//variable with the y-coordinates, which are randomly fetched from the array
var moveTop = items2[Math.floor(Math.random()*items2.length)];

// y-achs movement
$(this).animate({ "top"  : ""+ moveTop  +"px" }, 800);  
// x-achs movement with the callback function which should add the colored div to every position the box has been
$(this).animate({ "left" : ""+ moveLeft +"px" }, 800, function(){

$("<div style='position: absolute; top: "+moveTop+"px; left: "+moveLeft+"px; background: #ffcccc; width: 50px; height: 30px;'></div>").appendTo("#grid");

});                     
//get the box to the initial position
$(this).animate({"left": "0px"}, 800);
$(this).animate({"top" : "0px" }, 800);
//mark cycle passed
i++;
1

There are 1 best solutions below

8
On

Your moveTop and moveLeft variables are being changed every time through the for loop so when the animation completes some time later, you only see the values at the end of the loop in the animation completion callback. You can freeze the values each time through the loop with a closure like this:

var i =0;

while( i<5 ){ //specifying the cycles for the box' movement

    var hstandWidth = $("#hstand").width()+27; //getting the current x-coordinates

    //array to randomly select x-coordinates from 
    var items = Array(hstandWidth-(50)*1, hstandWidth-(50)*2, hstandWidth-(50)*3, hstandWidth-(50)*4, hstandWidth-(50)*5,hstandWidth-(50)*6,hstandWidth-(50)*7,hstandWidth-(50)*8);                                         
    //variable with the x-coordinates, which are randomly fetched from the array
    var moveLeft = items[Math.floor(Math.random()*items.length)];

    //array to randomly select y-coordinates from 
    var items2 = Array(30,30*2,30*3,30*4,30*5,30*6,30*7);
    //variable with the y-coordinates, which are randomly fetched from the array
    var moveTop = items2[Math.floor(Math.random()*items2.length)];

    // y-achs movement
    $(this).animate({ "top"  : ""+ moveTop  +"px" }, 800);  
    // x-achs movement with the callback function which should add the colored div to every position the box has been

    // create closure to capture moveTop and moveLeft values for later callback
    // use different named variables xmoveTop and xmoveLeft to distinguish the
    // closure variable from the outer for loop variable
    (function(obj, xmoveTop, xmoveLeft) {
        $(obj).animate({ "left" : ""+ xmoveLeft +"px" }, 800, function(){

            $("<div style='position: absolute; top: "+xmoveTop+"px; left: "+xmoveLeft+"px; background: #ffcccc; width: 50px; height: 30px;'></div>").appendTo("#grid");

        });
    })(this, moveTop, moveLeft);                     

    //get the box to the initial position
    $(this).animate({"left": "0px"}, 800);
    $(this).animate({"top" : "0px" }, 800);
    //mark cycle passed
    i++;
}