Reputation: 2413
The code in question:
function addLinks () {
for (var i=0, link; i<5; i++) {
link = document.createElement("a");
link.innerHTML = "Link " + i;
link.onclick = function (num) {
return function () {
alert(num);
};
}(i);
document.body.appendChild(link);
}
}
window.onload = addLinks;
My problem is since the returned function is a closure and variable num
is the global variable each time the function executes num
should hold the current value replacing the old one and should automatically update with that value everywhere. But this isn't happening -- when I click each link I get a different value. Why is this happening?
Upvotes: 4
Views: 157
Reputation: 76
You haven't explained where the variable num
has come from or how it's used. I'm guessing you mean to alert the current value of i
. The click handler takes an event object as a parameter so I would try it like this:
function addLinks () {
for (var i=0; i<5; i++) {
var link = document.createElement("a");
link.innerHTML = "Link " + i;
link.onclick = function (event) {
alert(i);
};
document.body.appendChild(link);
}
}
window.onload = addLinks;
Upvotes: 0
Reputation: 15351
This is strange and disagrees with what I thought to knew about closures. With the only change (in the first version of addLinks
in the question):
link.onclick = function (num)
to
link.onclick = function ()
You get the expected result, that is, the actual value of the global num
variable is alerted any time when a link is clicked.
It probably has to do with how the interpreter saves scope variables that are referenced within a closure when it encounters that closure. When a variable is referneced in a closure, the closest occurrence of that variable is searched for going upwards from the current scope.
While in the first case it is defined as a parameter (to the function that is called after declaration multiple times) it has different value each time so a different scope value is "remembered" by each closure.
In the 2nd case, the only occurrence found is in the global scope which results in the actual value of num
is being used regardless which handler is called.
Upvotes: 0
Reputation: 14747
Think about it: what if you had three links like so:
<a href="#">0</a>
<a href="#">1</a>
<a href="#">2</a>
You want them to alert out their number when you click on them, so you do it like this:
var links = $('a');
for (var i = 0; i < links.length; i++) {
links[0].onclick = function () {
alert(i);
}
}
At first glance, you'd expect that, for example, since you assigned the click handler on the first link when i = 0
, it'll alert 0
when you click it. However when you click it, it'll actually alert 3
.
You said it yourself, your code is creating a closure. What the code above does is that it's assigning a function handler to the click event of each link. Each of those function handlers is maintaining a reference to the variable i
(note: not it's current value!).
At the point when you assign the function handler, it actually doesn't evaluate what value i
has (because it doesn't need it). When you click, aha, that's when it checks what value i
has and alerts it.
By the time you click a link, your for
loop will be long finished, with i = 3
, and that's what your click handler alerts.
Upvotes: 1
Reputation: 191729
num
is closed over by the anonymous function. The function (num) { return function () {}}(i)
passes i
as it is to the inner function and returns a new function based on whatever i
's value is at the time.
If you want the click callback to always alert the maximum value of i
, it's actually even easier:
link.onclick = function () {
alert(i);
}
function (var varname)
is simply invalid syntax.
Upvotes: 2
Reputation: 7898
Please look at the (i) after your function. This type of notation is for self-invocating functions only. It's as if you are setting link.onclick = a number, whereas it expects a function. You can simply use the following.
link.onclick = function (event) {
event.preventDefault();
alert(i);
};
Please note that click functions receive the "event" as a parameter by default. Make sure you call the preventDefault() method of the event otherwise it will bubble up the DOM and trigger a postback due to the nature of the anchor element.
Upvotes: 0