views:

165

answers:

4

Hi,

This is a really basic question but...

I have some code like this

var arr = Array('blah.jpg','ha.jpg');
for (var i=0; i<array.length; i++)
{
    $('div#blah' + i).click(function() {
           $('img').attr('src', arr[i]); });
}

This should bind the div with id="blah0" to change all images to 'blah.jpg' when clicked. Similarly, clicking the div with id ="blah1" should change all images to 'ha.jpg'.

However, the anonymous function won't work because it will use the value of 'i' at the time of execution, i.e. 2. This means that clicking either div will try and set all images to arr[2] - a non-existent element (interestingly not throwing a JS error on my machine but that's another story...).

How can I get the anonymous function to be created using the value of 'i' at declaration time?

As a simpler example:

for (var i=0; i<10; i++)
{
    $('div#blah'+i).click(function() {
       alert(i)); });
}

This should display '0' when I click 'blah0', '1' when I click 'blah1' etc.

However, by default it will display '10' no matter which 'blah' i click.

A: 

Have 1 more variable inside the loop and increment it after using it in the closure.


var j = 0;
for (var i=0; i<array.length; i++)
{
    $('div#blah' + j).click(function() {
           $('img').attr('src', arr[i]); });

    j++;
}

shahkalpesh
The problem isn't binding (i.e. 'div#blah' + j) but the array access (i.e. arr[i]). In the method you use, there will be no difference at all - the click will still call call the 'i'th element of arr using the value of i at that time (e.g. array.length) not the value of i when i made the function
Graphain
shahkalpesh
I haven't tried this. But is new Function() {...} possible?
shahkalpesh
Your second option could probably work. Not sure on new function() {}.
Graphain
+5  A: 

Declare a new variable inside a function that creates a new click handler that gets the current value of i as a parameter:

function makeClickHandler(arr, local_i) {
    return function() {
        $('img').attr('src', arr[local_i]);
    };
}

var arr = Array('blah.jpg','ha.jpg');
for (var i=0; i<array.length; i++)
{
    $('div#blah' + i).click(makeClickHandler(arr, i));
}

Each instance of the function gets its own copy of local_i, that doesn't change each time.

Matthew Crumley
I'll try this now - but won't this still access the value of i each time I click (i.e. set local_i to value that 'i' is at the the click time?)
Graphain
Yep, that's what I'm getting.
Graphain
Oops, I guess I didn't pay attention enough to the the context.
Matthew Crumley
Thanks for your answer but yeah :-)
Graphain
It *should* work now... I haven't actually tried it though.
Matthew Crumley
Yep works :-) Thanks!
Graphain
A: 

I have this answer so far but bit of a hack:

var arr = Array('blah.jpg','ha.jpg');
for (var i=0; i<array.length; i++)
{
    eval("$('div#blah' + i).click(function() { $('img').attr('src', arr[" + i + "]); })");
}

Also:

for (var i=0; i<10; i++)
{
    eval("$('div#blah'+i).click(function() { alert(" + i + ")); });");
}
Graphain
Could also use the multiline hack as here http://forums.whirlpool.net.au/forum-replies-archive.cfm/487804.html if too long to single line eval.
Graphain
+3  A: 

In this particular case you should be using a closure:

for (var i=0; i<10; i++)
{
    (function(j){
        $('div#blah'+j).click(function() { alert(j)); });
    })(i);        
}

(function(){ /* code */ })() denotes a self executing function, which implies that it will use and evaluate the value of i immediately in the loop as opposed to at click time.

Darko Z
Ah nice. Thanks!
Graphain