views:

46

answers:

2

Hello, I have such situation: There is 8 div-blocks with ids like 'rateN_wrapper' where is 'N' is the number of div:

<div id="rate1_wrapper">
  <a href="#" id="0_1">...</a>
  <a href="#" id="0_2">...</a>
  <a href="#" id="0_3">...</a>
</div>

<div id="rate2_wrapper">
  <a href="#" id="1_1">...</a>
  <a href="#" id="1_2">...</a>
  <a href="#" id="1_3">...</a>
</div>

...

var ratings = new Array();
for (i=0; i < 8; i++)
{
    ratings[i] = -1; // Default is unrated

}

for (i=0; i < 8; i++)
{
    $('#rate' + i + '_wrapper a').click(function() {
        ratings[i] = parseInt( $(this).attr('id').split('_')[1] );
        console.debug(ratings);
    });
}

My work is to fill array in need place with clicked link's id (parsed). But it's always changes only latest element of array (8). Why?

+1  A: 

Because the function you create is closing over the i variable. It sees a reference to i at its current value instead of the value when you created the function. After the for loop exits, i will be 8, so all your anonymous functions will update ratings[8]. I think this might fix it:

for (i=0; i < 8; i++)
{
    var idx = i;
    $('#rate' + idx + '_wrapper a').click(function() {
        ratings[idx] = parseInt( $(this).attr('id').split('_')[1] );
        console.debug(ratings);
    });
}

I'm not sure if declaring the var inside the loop body will rebind it for every iteration. If so, then the anonymous function will only see the value that idx had at the time the function was created. Here is a way that I know will work:

function CreateHandler(idx) {
    return function() {
        ratings[idx] = parseInt( $(this).attr('id').split('_')[1] );
        console.debug(ratings);
    }
}

for (i=0; i < 8; i++) {
    $('#rate' + idx + '_wrapper a').click(CreateHandler(i));
}

So you create a function that will create an anonymous function with the correct index. The returned anonymous function will see the value of idx at the time of its creation.

A. Levy
It would also be possible to avoid the for loop completely by using jquery .each() which might be more clear
jarrett
This didn't help me.
Ockonal
Actually, come to think of it, the first example will work if you declare var idx = i inside the anonymous function body instead of the outer loop body...
A. Levy
It doesn't work, or it is confusing?
A. Levy
@jarrett I can't use each() here, I haven't static name of object.
Ockonal
You don't need a static name to use each
jarrett
+2  A: 

That's a problem caused by the closure in the for loop. You could lookup the id by parsing the parent id:

for (i=0; i < 8; i++)
{ 
  $('#rate' + i + '_wrapper a').click(function() {
    var parentId = $(this).parent('div').attr('id');
    var index = /\d/.exec(parentId);
    ratings[index] = parseInt( $(this).attr('id').split('_')[1] );
  });
}
Jan Willem B