0

Following part of my javscript(using jquery).

list = ['a', 'b', 'c'];
for(var i = 0 ; i< list.length ; i++) {
   $("<a>click here</a>").
      click(function(){
          foo(list[i]);
      }).
      appendTo('#sometag');
}
function foo(val) {
    console.log(val);
}

always prints c, no matter which tag you click on. How do I print proper value ???
It seems that it is using the last value of i=3 and thus evaluating o c always

Kevin Bowersox
  • 93,289
  • 19
  • 159
  • 189
Kaunteya
  • 3,107
  • 1
  • 35
  • 66
  • 1
    Side note, `int i = 0` is wrong. Try `var i = 0` or just `i = 0`. – j08691 Feb 01 '14 at 15:46
  • And technically, the code you have above doesn't even log "c", it logs "undefined". http://jsfiddle.net/j08691/9Th9G/ – j08691 Feb 01 '14 at 15:51
  • Dude it is just a sample code. People have already answered. Instead if finding mistakes above this, put some time below this. – Kaunteya Feb 01 '14 at 15:58
  • Dude, post code that does what you say it does. It wastes everyone's time debugging your code when you post inaccuracies. – j08691 Feb 01 '14 at 16:02
  • possible duplicate of [Javascript closure inside loops - simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – Felix Kling Feb 01 '14 at 16:56

2 Answers2

6

You'll need a closure new scope as the iteration finishes before the event handler is triggered, so when the click happens, the loop has finished and i is the last value it's set to, a new scope keeps the value of i local to that scope

list = ['a', 'b', 'c'];

for(var i=0; i<list.length; i++) {

   (function(j) {

       $("<a>click here</a>").click(function(){
          foo(list[j]);
       }).appendTo('#sometag');

   }(i));

}

function foo(val) {
    console.log(val);
}

Another option is to add more jQuery

list = ['a', 'b', 'c'];

$.each(list, function(index, item) {
    $("<a />", {text : 'click here',
                on   : {
                    click : function() {
                        foo(item);
                    }
                }
    }).appendTo('#sometag');
});

function foo(val) {
    console.log(val);
}
adeneo
  • 312,895
  • 29
  • 395
  • 388
  • I never thought of using the self executing function to form a closure. Good idea. Is that preferable to forming the closure in a function defined elsewhere? – Kevin Bowersox Feb 01 '14 at 15:50
  • The event handler *already* is a closure. The solution is to create a new scope by executing a function, not by creating another closure. – Felix Kling Feb 01 '14 at 16:53
  • @FelixKling - Yup, but most people tend to understand it better when you just say "closure", and not scope. – adeneo Feb 01 '14 at 17:23
3

Create a closure to retain the value of i for the particular iteration when the function is executed, without a closure i remains in scope causing all of the created functions to eventually have the value at the last iteration:

var list = ['a', 'b', 'c'];
for(var i = 0 ; i< list.length ; i++) {
   var func = createFoo(list[i]);
   $("<a>click here</a>").
      click(func).
      appendTo('#sometag');
}
function createFoo(value){
    return function(){
        foo(value);
    };
}
function foo(val) {
    console.log(val);
}

JS Fiddle: http://jsfiddle.net/5dUgw/

Also note you need to change int to var since this is JS.

Kevin Bowersox
  • 93,289
  • 19
  • 159
  • 189
  • The event handler *already* is a closure. The solution is to create a new scope by executing a function, not by creating another closure. – Felix Kling Feb 01 '14 at 16:54