1

I've got an ajax request which returns me JSON data (array of objects). Iterating this array I generate a list of links. I'd like to set an onClick event and call a function with params from ajax response.

It may sound not clear, so here is an example:

function test(param1) {
    // ...
}

$.ajax({
    url: 'getSomeData.php',
    type: 'post',
    data: data,
    success: function(result){
        var result = JSON.parse(result);
        $div = $('<div />');

        for (i in result.someObject) {
            $div.append('<a href="#" id="linkID' + i + '">ajax link ' + i + '</a>');
            $('#linkID' + i).click(function(){
                test(result.someObject[i]); // <--- My question is here
                return false;
            });
        }
        $(document.body).append($div);
    }
});

In my function test I always get the last object of iterated array. I have some idea why this happens, but I don't know how to fix it.

1
  • @Anonymoose true, but irrelevant for the bug described. It would fail just the same if it was declared. Commented Apr 27, 2012 at 10:23

3 Answers 3

4

It's a problem with the scope of i, if you use a closure to restrict its scope to the click event handler like below it should be fine.

(function(i) {
  $('#linkID' + i).click(function(){
    test(result.someObject[i]); // <--- My question is here
    return false;
  });
})(i);

This guarantees that i is at the value it was when the handler was created instead of being the last value it was set at.

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

2 Comments

Is this correct: (function(i, j){ /* ... */ }(i, j); if I want to restrict the scope of more than one variable? I actualy do not understand the meaning of the last part })(i); What does it do?
@SaltLake yes, that's the correct syntax for multiple parameters. The final part just calls that function immediately with the supplied parameter. Adding multiple parameters is also trivial with the eventData method - you just add them to the map.
2

Your problem is one of scoping - your variable i is not bound in the scope so by the time the event handler is called it always has the last value assigned.

The simplest method to resolve this (as you're using jQuery) is to use the eventData parameter to .click():

('#linkID' + i).click({ param1: result.someObject[i] }, test);

and then change test to read the event data:

function test(ev) {
    var param1 = ev.data.param1;
    // ...
}

jQuery will bind any eventData supplied and automatically add it to the event object passed to the event handler.

This avoids have to create any additional closures to fix the scope of the supplied parameter.

See the section entitled "Passing Event Data" at http://api.jquery.com/bind/

Comments

0

You can pass a context (and thus variables) by using jQuery.proxy()

Simple example:

MyObject = function() {
  this.element = $('#element');
  this.some_var = 'It works!';

  this.element.click($.proxy(this.handleClick, this));
};

MyObject.prototype.handleClick = function() {
  console.log(this.some_var);
};

var m = new MyObject();

Example from the jQuery docs:

<script>
var me = {
  type: "zombie",
  test: function(event) {
    // Without proxy, `this` would refer to the event target
    // use event.target to reference that element.
    var element = event.target;
    $(element).css("background-color", "red");

    // With proxy, `this` refers to the me object encapsulating
    // this function.
    $("#log").append( "Hello " + this.type + "<br>" );
    $("#test").unbind("click", this.test);
  }
};

var you = {
  type: "person",
  test: function(event) {
    $("#log").append( this.type + " " );
  }
};

// execute you.test() in the context of the `you` object
// no matter where it is called
// i.e. the `this` keyword will refer to `you`
var youClick = $.proxy( you.test, you );


// attach click handlers to #test
$("#test")
  // this === "zombie"; handler unbound after first click
  .click( $.proxy( me.test, me ) )
  // this === "person"
  .click( youClick )
  // this === "zombie"
  .click( $.proxy( you.test, me ) )
  // this === "<button> element"
  .click( you.test );
</script>

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.