5

JavaScript's late binding is great. But how do I early bind when I want to?

I am using jQuery to add links with event handlers in a loop to a div. The variable 'aTag ' changes in the loop. When I click the links later, all links alert the same message, which is the last value of 'aTag'. How do I bind a different alert message to all links?

All links should alert with the value that 'aTag' had when the event handler was added, not when it was clicked.

for (aTag in tagList) {
  if (tagList.hasOwnProperty(aTag)) {
    nextTag = $('<a href="#"></a>');
    nextTag.text(aTag);
    nextTag.click(function() { alert(aTag); });
    $('#mydiv').append(nextTag);
    $('#mydiv').append(' ');
  }
}
Sven Larson
  • 386
  • 2
  • 8

3 Answers3

4

You can pass data to the bind method:

nextTag.bind('click', {aTag: aTag}, function(event) {
    alert(event.data.aTag);
});

This will make a copy of aTag, so each event handler will have different values for it. Your use case is precisely the reason this parameter to bind exists.

Full code:

for (aTag in tagList) {
  if (tagList.hasOwnProperty(aTag)) {
    nextTag = $('<a href="#"></a>');
    nextTag.text(aTag);
    nextTag.bind('click', {aTag: aTag}, function(event) {
      alert(event.data.aTag);
    });
    $('#mydiv').append(nextTag);
    $('#mydiv').append(' ');
  }
}
interjay
  • 107,303
  • 21
  • 270
  • 254
1

You can also make a wrapper function that takes the text to alert as a parameter, and returns the event handler

function makeAlertHandler(txt) {
  return function() { alert(txt); }
}

and replace

nextTag.click(function() { alert(aTag); });   

with

nextTag.click(makeAlertHandler(aTag));
Jonas Høgh
  • 10,358
  • 1
  • 26
  • 46
-2

You need to keep a copy of this variable, like this:

for (aTag in tagList) {
  if (tagList.hasOwnProperty(aTag)) {
    nextTag = $('<a href="#"></a>');
    nextTag.text(aTag);
    var laTag = aTag;
    nextTag.click(function() { alert(laTag); });
    $('#mydiv').append(nextTag);
    $('#mydiv').append(' ');
  }
}

The aTag variable is changing each time you loop, at the end of the loop it's left as the last item in the loop. However, each of the functions you created point at this same variable. Instead, you want a variable per, so make a local copy like I have above.

You can also shorten this down a lot with chaining, but I feel it clouds the point in this case, since the issue is scoping and references.

Nick Craver
  • 623,446
  • 136
  • 1,297
  • 1,155
  • Won't the same problem happen with the variable `laTag`? – interjay Apr 18 '10 at 19:33
  • @interjay - Nope, that variable is **inside** the closure, the **same** reference isn't updated every time like the variable in the for delcaration, it's a **different** variable each loop, which is what you want. – Nick Craver Apr 18 '10 at 19:34
  • But there is no closure here - it's just a `for` loop. – interjay Apr 18 '10 at 19:36
  • @interjay - I was referring to the anonymous function for the click handler, you're creating a distinct reference to pass inside that closure whereas the original code doesn't and passes the reference of a changing variable (which is why you see the last value of the `for`). – Nick Craver Apr 18 '10 at 19:39
  • It is more tricky. I tried your code (in fact I tried it before, because I thought the same) and now I tried it again. It does not solve the problem, always the last value is shown. Strange! – Sven Larson Apr 18 '10 at 19:40
  • @Nick, there is only one variable `laTag` used by all the click handlers, because it is defined outside the handler. I tested this to confirm. – interjay Apr 18 '10 at 19:43
  • This approach would work inside a `$.each` loop, since that involves creating a function which creates a closure for each iteration. – interjay Apr 18 '10 at 19:48
  • @interjay - Doh, you're correct, I was testing in a `$.each`, forgot it creates one: http://jsfiddle.net/cy2HJ/ – Nick Craver Apr 18 '10 at 19:51
  • @Sven - You can do this using `$.each()` with the closure a bit cleaner, like this: http://jsfiddle.net/NAzeQ/ – Nick Craver Apr 18 '10 at 19:58