George Filippakos
George Filippakos

Reputation: 16569

Javascript variable scope issue with jquery click event

I am trying to assign a series of objects stored in an array to jquery click event handlers.

The problem is , when the event fires, I only ever references the last object in the array.

I have put together a simple example to show the problem:

function dothis() {
    this.btns = new Array('#button1', '#button2');
}

// Add click handler to each button in array:
dothis.prototype.ClickEvents = function () {

    //get each item in array:
    for (var i in this.btns) {

        var btn = this.btns[i];
        console.debug('Adding click handler to button: ' + btn);

        $(btn).click(function () {
            alert('You clicked : ' + btn);
            return false;
        });
    }
}

var doit = new dothis();
doit.ClickEvents();

The HTML form contains a couple of buttons:

<input type="submit" name="button1" value="Button1" id="button1" />
<input type="submit" name="button2" value="Button2" id="button2" />

When button1 is clicked, it says "You clicked #Button2"

It seems that both button click handlers are pointing to the same object inside var btn.

Considering the variable is inside the for loop, I cannot understand why.

Any ideas?

Upvotes: 0

Views: 7245

Answers (5)

seutje
seutje

Reputation: 711

within an event handler, 'this' usually refers to the element firing the event, in this case, it would be your button

so the solution to your problem is fairly easy, instead of referencing the btn variable, which lives in a higher scope and gets mutated long before the event handler fires, we simply reference the element that fired the event and grab its ID

$(btn).click(function () {
  alert('You clicked : #' + this.id);
  return false;
});

Note: if your array contains other selectors that just the ID, this will obviously not reflect that and simply continue to show the ID

Lucky, the click handler (and all other event handlers afaik) take an extra parameter for eventData, useful like so:

$(btn).click(btn, function (event) {
  alert('You clicked : #' + event.data);
  return false;
});

User an array if you're passing multiple things:

$(btn).click(['foo', 'bar'], function (event) {
  alert('this should be "foo": ' + event.data[0]);
  alert('this should be "bar": ' + event.data[1]);
  return false;
});

Upvotes: 2

Mike Haboustak
Mike Haboustak

Reputation: 2296

You need a function factory to close the loop variable, such as this:

//get each item in array:
for (var i=0; i<this.btns.length; i++) {

    $(this.btns[i]).click(function(item) { 
        return function () {
            alert('You clicked : ' + item);
            return false;
        }
    }(this.btns[i]));

}

Another good option is to let jquery help you. Use jQuery.each(). The variable btn here is local to the handler function, and so isn't reused between iterations. This allows you to close it and have it keep its value.

$.each(this.btns, function() {
    var btn = this;
    $(this).click(function () {
        alert('You clicked : ' + btn);
        return false;
    }
});

Upvotes: 4

BNL
BNL

Reputation: 7133

Your problem is here:

alert('You clicked : ' + btn);

btn retains the value from the last time it was called in the loop. Read the value from the button in the event.

    $(btn).data('selector', btn).click(function () {
        alert('You clicked : ' + $(this).data('selector'));
        return false;
    });

http://jsfiddle.net/Mc9Jr/1/

Upvotes: 0

Shlomi Komemi
Shlomi Komemi

Reputation: 5545

maybe you need to change just the click binding:

$(btn).click(function () {
    alert('You clicked : ' + $(this).attr('id'));
    return false;
});

Upvotes: 0

Vlad Balmos
Vlad Balmos

Reputation: 3412

you have to use closures for this. i'm not sure if i remember the correct syntax but you could try this:

$(btn).click(function () {
    return function() {
        alert('You clicked : ' + btn);
        return false;
    }
});

Upvotes: 0

Related Questions