1

Consider this code:

for( var i = 1; i <= 2; ++i )
{
 $( '#id' + i ).click(
   function()
   {
     alert( 'ID = ' + i );
   }
  )
}

The problem is that when id1 or id2 is clicked the alert always says ID = 3. Now I know why this happens (it is evaluated at call time), I am just curious if there is any way to prevent that? (1)

Many questions here and articles in general explain the concept of closure quite well, I have however been unable to find any technique to avoid it.

On a side note, I tried to use (note the extra keyword "new"):

new function() { alert( 'ID = ' + i ) }

however that calls the alert directly at the time it is defined and does nothing on click events. Any idea on this? (2)

j08691
  • 204,283
  • 31
  • 260
  • 272
NeverStopLearning
  • 958
  • 11
  • 21

3 Answers3

2

One way would be like this.

HTML

<div id="id1">ID1</div>
<div id="id2">ID2</div>
<div id="id3">ID3</div>

Javascript

for (var i = 1; i <= 2; i += 1) {
    (function (j) {
        $('#id' + j).click(

        function () {
            alert('ID = ' + j);
        });
    }(i));
}

On jsfiddle

Xotic750
  • 22,914
  • 8
  • 57
  • 79
1

Basically you need to create a new scope where the value of i doesn't change. So to illustrate, you could create the function outside the loop, passing the current value:

function createClickAction(i){
    return function(){
        alert( 'ID = ' + i );
    }
}

for(var i = 1;i <= 2; ++i){
    $( '#id' + i ).click(
        createClickAction(i)
    );
}

But a shorter way to do the same thing is to pass the value into an IIFE:

for(var i = 1;i <= 2; ++i){
    (function(i){
        $( '#id' + i ).click(
            function(){
                alert( 'ID = ' + i );
            }
        );
    })(i);
}

Here is some discussion about the syntax new function(){...}
It has the same effect as an IIFE because you are instantiating an anonomous function, as a constructor. So it's like saying:

function Message(){
    alert('hi');
}
new Message();

...but without the naming.
And to pass arguments in:

new function(msg){
    alert(msg)   
}('hi');

So you were probably close to finding this solution:

for(var i = 1;i <= 2; ++i){
    new function(i){
        $( '#id' + i ).click(
            function(){
                alert( 'ID = ' + i );
            }
        );
    }(i);
}
Community
  • 1
  • 1
robC
  • 2,592
  • 1
  • 23
  • 28
0

how about -

for( var i = 1; i <= 2; ++i )
{
 $( '#id' + i ).click(
   function()
   {
     alert( 'ID = ' + this.id.substring('#id'.length) );
   }
  )
}
Moazzam Khan
  • 3,130
  • 2
  • 20
  • 35
  • Thanks. This (most likely) works for my given example. I was, however, interested in the solution concept in general (e.g. see robC's answer). – NeverStopLearning Aug 30 '13 at 15:15