views:

40

answers:

2

Consider the following example:

function async_callback(array1, array2, array3) {
  // All arrays are equal length

  for(var i = 0; i < array1.length; i++) {
    var myElement = new Element('div', { 'id': 'dvMy' + i, 'events': { 'click': function() { SomeFunction(array1[i], array2[i], array3[i] } }});
    $(document).appendChild(myElement);
  }
}

Now when I click my element, I get a null value for all three arguments. I even tried doing: myElement.onclick = SomeFunction; // but this won't allow arguments I know I can create a string and use eval() and that does work, but I don't like eval().

Any ideas???

btw: This is a simple example to replicate the problem, and not actual code.

+1  A: 

This is a classic: when the click handler runs, i is set to the last value it reached in the loop, which in this case is array1.length. To get what you want, you need to call a function to create the click handler and pass it the value of i:

function async_callback(array1, array2, array3) {
  function createClickHandler(n) {
    return function() {
      SomeFunction(array1[n], array2[n], array3[n]);
    };
  }

  // All arrays are equal length
  for(var i = 0; i < array1.length; i++) {
    var myElement = new Element('div', {
      'id': 'dvMy' + i,
      'events': {
        'click': createClickHandler(i)
      }
    });
    $(document).appendChild(myElement);
  }
}
Tim Down
+2  A: 

This is your handler:

function() { SomeFunction(array1[i], array2[i], array3[i] } }

By the time its executed, the loop will have completed and i will therefore be equal to the length of the array, as per the conditional statement (i < array1.length) and the iteration statement (i++).

The easiest way to resolve this is to wrap the handler in an additional immediately-executed function, and pass i to it, -- this will have the effect of retaining the value of i in a new closure:

for(var i = 0; i < array1.length; i++) {
    var myElement = new Element('div', { 'id': 'dvMy' + i, 'events': {
        'click': (function(i){
            return function() { SomeFunction(array1[i], array2[i], array3[i] } };
         }(i))
    });
    $(document).appendChild(myElement);
}
J-P