3
<div id="image_cont">
  <img src="images/pic1.jpg" alt="pic1" />
  <img src="images/pic2.jpg" alt="pic2" />
  <img src="images/pic3.jpg" alt="pic3" />
</div>

$(document).ready(function() {
    slide(3, "image_cont", 5000, 600);
});

function slide(numberOfImages, containerId, timeDelay, pixels) {
    //start on first image
    var i = 0;
    var style = document.getElementById(containerId).style;
    window.setInterval(function() {
        if (i >= numberOfImages){
            i = 0;
        }
        var marginLeft = (-600 * i);
        var pixelMovement = pixels/15;


////////////////////////////////////////LOOK HERE//////////////////////////////


        for (var j = 0; j * pixelMovement < 600; j++){
            window.setTimeout(function(){
//alert('marginLeft: ' + marginLeft + ' j: ' + j + ' pixelMovement: ' + pixelMovement);
//this alert shows j is 15 when it should be 0, what's going on?


/////////////////////////////////////////END//////////////////////////////////
                style.marginLeft = (marginLeft - j * pixelMovement) + "px";
                }, 150);
        }
        i++;
    }, timeDelay);
}
3
  • Could add the HTML of the page? Commented Aug 27, 2011 at 9:06
  • it seems to work fine for me, but couldn't you replace document.getElementById(containerId) with $('containerId')? Commented Aug 27, 2011 at 9:11
  • There is only variable named j <-- understand that and understand the issue :) This is a highly duplicated question on SO and a slightly confusing aspect of closures in JavaScript. Remember, closures bind to free-variables (not values) and only a new function scope can introduce new variables. Commented Aug 27, 2011 at 9:12

3 Answers 3

6

You can't use the variable j directly in the setTimeout function because that function runs some time later after your for loop has completed and thus j has the terminating value, not the value when you called setTimeout.

You can capture the current value of j in a function closure that would be available in the setTimeout function like this:

for (var j = 0; j * pixelMovement < 600; j++){
    window.setTimeout(function(cntr) {
        return function() {
            style.marginLeft = (marginLeft - cntr * pixelMovement) + "px";
        };
    } (j), 150);
}

I find this type of closure kind of confusing. I'll try to explain what's happening. We pass to the setTImeout() function the result of executing an anonymous function that takes one parameter (which I named cntr here) and we pass the value of j as the value of that parameter. When that function executes (which now has the value of j available inside it), that function returns another anonymous function. This other anonymous function is what setTimeout will actually call when it fires. But, this second anonymous function is inside a function closure from the first function that has the captured value of j in it (as a variable that I renamed cntr while inside the function closure to avoid confusion in explaining it). It's the anonymous functions that make it so confusing, but it works.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the lesson, I think I mostly understand it. Unfortunately my script is still behaving in an unexpected manner. My wizardry is not strong enough.
1

It should be anything, because window.setTimeout is an "async" function so it returns immediately after executing.

So in your code the for loop keeps looping and after a while (150ms) your function is being executed and the j variable's actual value is printed out.

Comments

0

The functions you create and pass to setTimeout are all referencing the same loop counter variable j - after the loop has run its course, j will be set at the value which caused the loop to terminate. You need to use a closure to ensure the functions you're creating in the inner loop have access to the value j had at the time the function was defined.

See this answer for more detail on the problem and the solution:

(Silly variable name to make it clear that it's distinct from j):

window.setTimeout((function(jWhenFunctionWasDefined) {
  return function() {
    style.marginLeft = (marginLeft - jWhenFunctionWasDefined * pixelMovement) + "px";
  }
})(j), 150)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.