Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
215 views
in Technique[技术] by (71.8m points)

jquery callback function only working on last loop

for (var i = 0; i < barValues.length; i++) {


    actualBarHeight = Math.floor((barValues[i] / chartMaxY) * barchartHeight);

    var barChartID = "#barChart" + (i + 1)
    $(barChartID + " .value span").css('background-color', 'transparent');
    $(barChartID + " img").animate({
        height: actualBarHeight
    }, 500, function () {
        $(barChartID + " .value span").css('background-color', 'white');
    }
    );

    $(barChartID + " .value span").html("$" + Math.floor(barValues[i]));
    $(barChartID + " .value").css("bottom", actualBarHeight + "px");
    $(barChartID + " .ylabel").html(chartMaxY);

}

The above bit of jQuery is inside a for loop. Each iteration of the loop does the following:

  • sets the background of a span
  • animates an object
  • upon finishing, resets the background of the span

I'm using a call back function to reset the background so it finishes the animation before doing so. However, it only ends up affecting the last span referenced in the for loop.

If I move that bit of code outside of the callback, then it effects every span through every iteration of the for loop, (but doesn't wait for the animation in that case)

I'm guessing the issue has to do with building the selector INSIDE the function INSIDE the animate function. Is there some bad syntax in my markup?

EDIT (per Russ's suggestion, I now include the full loop in the above sample)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is a common problem experienced when combining closures with loops. JavaScript is a late-binding language and loops do not introduce a new scope. So:

for (var i= 0; i<5; i++) {
    $('#thing'+i).click(function() {
        alert(i);
    });
}

There is only one i variable in this code. It starts at 0 and once the assignment-loop is finished is left at 5. The click event on the #thing0 element is only ever going to be fired after the loop has finished executing, by which point the value of i will be 5. You will not get the define-time value 0 which you might have expected.

This applies not only to the loop variable itself but to any other variables you are re-assigning for each time round the loop too. So in your example the value of barChartID inside the animation callback function will always be the id associated with the last element in your loop.

The usual workaround is to take a copy of the loop variable's value at define-time by using a structure that does introduce a new scope, namely another function:

$(barChartID + " img").animate({height: actualBarHeight}, 500, function(barChartID) {
    return function() {
        $(barChartID + " .value span").css('background-color','white');
    };
}(barChartID));

More on looped closures.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...