11

Can someone tell me what I am doing wrong here? I simplified it below but I am basically trying to create a list and have a click event that references a variable only available in the loop.

for (var i = 0; i < data.length; i++) {                                                                           
  $newRow = $(rowFormat);                    
  $('a:first', $newRow).click(function(i){
    return function() { alert(i); } 
  });
  $list.append($newRow);      
}
4
  • The thing you did "wrong" is called closure. You gave every click function a refference to the same variable i. Commented Apr 4, 2011 at 15:06
  • @ITroubs: He clearly knew about that and tried to work around it (in fact, he didn't gave any of them a reference to the i used in the loop; he shadowed it in the [outer] anonymous function's argument list). He just forgot some parentheses -- see David's answer. :-) Commented Apr 4, 2011 at 15:16
  • yeah you are right. didn't read the code correctly ;-) Commented Apr 4, 2011 at 15:44
  • Yeah bad variable name choices... Commented Apr 5, 2011 at 13:13

3 Answers 3

14

You aren't calling the outer function.

$('a:first', $newRow).click(function(j){
  return function() { alert(j); } 
}(i)); /* Pay special attention to this line, it is where the major change is */

As T.J. Crowder mentioned, you can move the factory out of the loop.

function my_factory(j) {
    return function() { 
        alert(j); 
    }; 
}

$('a:first', $newRow).click(my_factory(i));
Sign up to request clarification or add additional context in comments.

6 Comments

By calling your own argument on click, you lose the event argument that is passed in.
+1 @Mike: Would recommend you move the factory function out of the loop, though; you're creating a bunch of extra factory functions unnecessarily, where you only need one (to create the various functions for click). Would also recommend looking at event delegation rather than creating a separate event handler for every row.
@Eli: That's not what the code's doing. The function accepting j isn't the click handler, it builds and returns the click handler. The click handler is the function that's calling alert.
It could be modified to return function(e) { alert(j); }, but since nothing was being done with the event object in the first place…
Exactly what I needed. If anyone else runs into this, I used this site to better my understanding of closures and function factories: developer.mozilla.org/en/JavaScript/Guide/Closures
|
3

You've almost got it, just one small change. This is actually my favorite example of a practical use of a closure in Javascript.

Basically, you need to create a function that takes a value, and returns a function that uses that value. See the commented line below for what your example is missing. Your code created the anonymous function, but didn't invoke it.

for (var i = 0; i < data.length; i++) {                                                                           
  $newRow = $(rowFormat);
  var fn = (function (value) {
    return function() {
      alert(value);
    };
  }) (i); //this is what you were missing, you need to invoke the anonymous function
  $('a:first', $newRow).click(fn);
  $list.append($newRow);      
}

Comments

0

Use 'bind' to attach the 'click' event and pass a parameter. Using 'event.data' you will be able to get the right value of your parameter:

Example 1:

 $(document).ready(function()
 {
    for (var i = 0; i < data.length; i++) 
    {                                                                           
        $newRow = $(rowFormat);                    
        $('a:first', $newRow).bind('click', {i: i},
            function (event)
            {
                alert(event.data.i);
            }
        );
        $list.append($newRow);      
    }
});

Example 2:

$(document).ready(function()
{
    $(".selectorA").each(function()
    {
        var elementId = $(this).attr("id");

        for(var i = 0; i < 20; i++) 
        {
            $('#question' + i).bind('click', {currentIndex: i, elementId: elementId}, 
                function (event)
                {
                   alert(event.data.currentIndex + " | " + event.data.elementId);
                }
            );
        }
    }
});

Comments

Your Answer

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.