We do a lot of jQuery DOM manipulation in our projects here at New Toronto Group. Often we are required to create applications with UIs that are generated on-the-fly, based on various runtime conditions.
One problem we see often is when we want to attach an event listener on a dynamically-created object, such as a button, and maintain a reference to that button’s index while it was created.
Sounds simple enough. Most developers would begin by coding something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | app.initBroken = function () { var times = 5, i; for (i = 0; i < times; i += 1) { var button = $('<button>I\'m button ' + i + '</button><br/>'); $('body').append(button); button.on('click', function (i) { alert(i); }); } }; |
So what happens when you click one of the buttons? That’s right – it alerts 5 every time. The alert function uses the i variable as it is when the button is clicked. Because the for loop completed when the button was clicked, i is 5. Dang…
So how do we capture the value of i at the time the button was created? This is where closures come in. We can leverage the fact that JavaScript functions each have their own scope. While we’re looping and creating the button, we can create a function which holds this value of i, and then returns it when the button is clicked. Something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | app.initFixed = function () { var times = 5, i; for (i = 0; i < times; i += 1) { var button = $('<button>I\'m button ' + i + '</button> '); $('body').append(button); button.on('click', function (j) { return function () { alert(j); }; }(i) ); } }; |
On line 15, we’re constructing a new function to return. Notice that we’re sending the function the value of i on line 18 when it is being created. It is this value that the function retains and returns when the button is clicked. (You may need to stare down this code for a few minutes before it sinks in.)
You can go one step further and move the inner function outside the for loop. This technique is illustrated in Douglas Crockford’s JavaScript: The Good Parts which inspired the solution presented here.






