0

I'm trying to create several links and bind an onclick handller to them inside a loop. On clicking the link, I want to display an alert box which indicates the link number (1 for 1st link, 2 for second link, 3 for second link and so on).

<html>
<body>
</body>
</html>

<script type = 'text/javascript'>
for (var i = 0; i < 10; i++) {
    var link = document.createElement("a");
    link.innerHTML = "Link " + i;
    link.href = '#';
    link.onclick = function () {
        alert("This is the link " + i);
        return false;
    };
    document.body.appendChild(link);
}
</script>

For some reason, I get the same alert message "This is the link 10" when I can click on any link.

Could it be because the parameters to the alert function get binded only when the function is called ? Because ultimately the value of i is 10 after the end of loop.

gdoron
  • 147,333
  • 58
  • 291
  • 367
Ashesh
  • 939
  • 2
  • 11
  • 28

1 Answers1

2

Simple answer, use closure:

link.onclick = (function(j) {
    return function(){
        alert ("This is the link " + j);
        return false;
    }
})(i);
Community
  • 1
  • 1
gdoron
  • 147,333
  • 58
  • 291
  • 367
  • Or better - move anonymous function out of loop at all – zerkms Jul 07 '13 at 22:44
  • Was gonna say same thing, but in examples like this i still have a hard time understanding why the closure is needed exactly. ..still correct response :) – Robert Hoffmann Jul 07 '13 at 22:44
  • @Robert, all the functions reference the same "global" `i` variable, if it change they reference the new value. you need to reference a private scope variable. hence the closure. – gdoron Jul 07 '13 at 22:46
  • what actually happens when you assign a self invoking function to a variable though ? Does it execute and then the returned function is assigned to link.onclick ? – Ashesh Jul 07 '13 at 22:51
  • 1
    The event handler in the OP's code is *already* a closure. The key here is to execute a function to create a new scope. That function does not have to be a closure (even though all functions are closures in JS). – Felix Kling Jul 07 '13 at 22:51
  • @Ashesh: `var value = (function() { ...}());` is the same as `function bar() {...}; var value = bar();`. You don't "assign" a "self invoking function". The function gets executed and the return value is assigned. – Felix Kling Jul 07 '13 at 22:52
  • @FelixKling, not sure what you meant at all. please explain(I'll read only tommrow as I must go right now). feel free to edit\delete it. I only answered to help the guy, and just linking seems like too deep for the op. – gdoron Jul 07 '13 at 22:55
  • People often provide this solution and describe it as "use a closure" but closures are actually not the reason why this solution works. A closure is simply a function together with an environment where non-local (free) variables are looked up. So technically, *every* function is a closure in JavaScript (because every function has access to the scope it was defined in). In the OP's code, the function that is assigned as event handler is a closure, because it has access to the loop variable `i`. Creating closures in a loop is a problem in JS because a closure keeps a reference to the variable – Felix Kling Jul 07 '13 at 23:06
  • binding instead of the value. This can only be solved by creating a new scope and "capture" the value of the loop variable. Hence the usage of an IIFE: `(function(j) {...}(i))`. The important aspect is that this expression creates a new scope and not that the function is a closure. In this counter example, I use a closure as well, but it does not solve the problem because no new scope is created: `link.onclick = function() { return function() {...};};`. Instead of executing the function, I just assign it to `onclick`. Hence the simple statement *"use a closure"* does not explain the solution. – Felix Kling Jul 07 '13 at 23:06
  • @FelixKling, thanks for the in-depth explanation. I see where I was wrong. thanks. – gdoron Jul 08 '13 at 08:02