views:

39

answers:

3

I have a code similar to this:

$.ajax({
        success: function(data) {
            text = '';
            for (var i = 0; i< data.length; i++) {
                text = text + '<a href="#" id="Data_'+ i +'">' + data[i].Name + "</a><br />";
            }            
            $("#SomeId").html(text);

            for (var i = 0; i< data.length; i++) {
                $("#Data_"+i).click(function() {
                    alert(data[i]);
                    RunFunction(data[i]);
                    return false;
                });                
            }
        }
    });

This gets an array of some data in json format, then iterates through this array generating a link for each entry. Now I want to add a function for each link that will run a function that does something with this data. The problem is that the data seems to be unavailable after the ajax success function is called (although I thought that they behave like closures). What is the best way to use the queried json data later on? (I think setting it as a global variable would do the job, but I want to avoid that, mainly because this ajax request might be called multiple times)

Thanks.

+1  A: 

Your problem is that the i variable is shared by the callbacks.

Therefore, all of the callbacks run on the last item.

The simplest solution is to use $.each:

        $.each(data, function(i) {
            $("#Data_"+i).click(function() {
                alert(data[i]);
                RunFunction(data[i]);
                return false;
            });                
        });

This will make a separate function call for each iteration, so there will be a separate i variable (or, in this case, parameter) for each iteration.

SLaks
this looks nice and easy
SztupY
+1  A: 

You can use .bind() directly and passing the data:

for (var i = 0; i< data.length; i++) {
    $("#Data_"+i).bind('click', {data: data[i]}, function() {
         alert(event.data.data);
          RunFunction(event.data.data);
          return false;
    });                
 }

I think you made a classical mistake, trying to generate functions in a loop. The variable i will have the same value for all functions but it is not even a valid array index anymore at the end of the loop.

See also JavaScript Closures for Dummies (no offense), example 5.

Felix Kling
no offense taken. I was sure that closures didn't work as I expected I just didn't knew how to make them work.
SztupY
A: 

SLaks answer is a good one, but he failed to explain why it wasn't working.

The problem is due to scoping. Try this out:

var logger = function(x){
  console.log(x);
};
for(var j = 0; j < 10; j++){
  window.setTimeout(function(){
    logger(j);
  }, 1000);
}

That nice little function prints out nothing but...9s! That's because the reference to j is kept by the timeout, so by the time the timeout runs, j is already set to 9.

Contrast that with:

var logger = function(x){
  console.log(x);
};
for(var j = 0; j < 10; j++){
  // we're wrapping our call in an anonymous function
  // don't do this in production code...make the function external instead
  // so you don't create 10 functions
  (function(k){
    window.setTimeout(function(){
      logger(k);
    }, 1000);
  })(j);
}

This version wraps the inner call in an anonymous function that takes as an argument the index. Since the scope of k is now limited to that function, the logger works as you'd expect.

jvenema