4

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?

Matt Fenwick
49.3k24 gold badges130 silver badges198 bronze badges
asked Feb 21, 2013 at 16:15
4
  • I don't understand your problem. Executing first version of addLinks in console and then clicking the links it adds results in each time a different value is alerted. Is this unexpected behaviour for you? Commented Feb 21, 2013 at 16:29
  • @Marcell Fülöp yaa completely unexpected as num is a global variable and its value should change or update each time the function runs Commented Feb 21, 2013 at 16:33
  • 1
    @Maizere num is not global. You don't need the var keyword when defining function parameters - they are implicitly local to that function. Commented Feb 21, 2013 at 16:42
  • @Marcell Fülöp thanks for the info.i thought num is a global variable Commented Feb 21, 2013 at 16:46

5 Answers 5

2

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.

answered Feb 21, 2013 at 16:23
Sign up to request clarification or add additional context in comments.

Comments

1

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.

answered Feb 21, 2013 at 16:29

Comments

0

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.

answered Feb 21, 2013 at 16:23

2 Comments

what are u telling ,u are out of the topic sir
"It's as if you are setting link.onclick = a number" no it's not -- it's setting it to a function.
0

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.

answered Feb 21, 2013 at 16:45

Comments

0

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;
Matt Fenwick
49.3k24 gold badges130 silver badges198 bronze badges
answered Feb 21, 2013 at 16:26

2 Comments

HTML elements do not have a .on method.
Yes you're right -- got jQuery mixed up in this. My apologies. So you could switch it back link.onclick = function(event) { alert(i); }; Thanks for pointing that out :-)

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.