Tuesday, April 6, 2010

Creating A Javascript Function Inside A Loop

I see the following question asked quite often in #javascript: "I loop over an array of elements and attach an event handler to each one. I pass along an index/variable for such & such reason. The problem is, when the event handler is executed, the index/variable is wrong!"


for (var i = 0, n = elements.length; i<n; i++) {
  var el = elements[i];
  el.addEventListener('click', function() {
    doSomethingWith(i, el); // i, el are not what you expect!
  }, false);

The reason that this is true is somewhat complex, but in basic terms, the function is only actually created once (instead of once each iteration of the loop) and that one function points to the last known values of the variables it uses. For more reading, start with closures and maybe move on to the ECMA Specification, in particular section 13.2, point 1.

The fix to this problem is not too difficult. There is a specific pattern that can be used to ensure that each iteration of the loop creates a brand-new function with the correct values. I call this pattern a "generator function". It probably has a proper name, but I'm not aware of it. The basic idea is to define a function (the generator) which creates and returns functions with the proper variables defined, and then call that generator function for each iteration of the loop, passing in the appropriate values. A typical generator function looks like this:

(function(variable) {
  return function() {
    // do something with variable 

There are a few things to notice in this pattern. First is that there are two function expressions: an outer one and an inner one. The outer one is the generator function, and the inner one is the function that contains your original code. Secondly, the generator function expression is wrapped in parenthesis and immediately called with an input of "value". This means that the inner function can use the identifier "variable" and it will refer to whatever value was passed in. The result of this whole shebang is a brand-new function which uses whichever values were passed in to the generator.

Applying this concept to our original problem, we come up with:

for (var i = 0, n = elements.length; i<n; i++) {
  var el = elements[i];
  el.addEventListener('click', (function(i, el) { 
    return function() {
      doSomethingWith(i, el);
  })(i, el), false);

It's quite close to the original code, but with a little bit of wrapping. Note that this pattern can be useful for more than just attaching event handlers, although in most cases some form of loop is involved.


  1. hello,
    your explanation

    "but in basic terms, the function is only actually created once (instead of once each iteration of the loop) and that one function points to the last known values of the variables it uses. For more reading, start with closures and maybe move on to the ECMA Specification, in particular section 13.2, point 1."

    is incomplete, it doesn't explain the behavior for implementations that *do not* join objects.

  2. Your "generator function" pattern saved my day. Thanks!

  3. I just arrived to the same conclusion after an evening of thinking... but thanks for the finer solution (I just created manually 3 different functions for the 3 iterations I needed :p )

  4. Many thanks for this trick !

  5. Have been looking for this for years maybe! :D

  6. thank you so much!

    I can't believe it took me so long to find this - but it was worthwhile!
    I was struggling a lot, as I'm somewhat novice to javascript, and was writing in coffeescript..
    Btw, the (single-line) expression I came up with to do the same with coffeescript is:
    el.addEventListener('click', ((i,el)->->doSomethingWith(i,el))(i,el) for el,i in elements

  8. You should avoid creating functions in loops. This is still creating a function, but it's one less per iteration.

    function something_wrapper(i, el) {
        return function() {
            doSomethingWith(i, el);
    var i,el; //for loops don't introduce a scope, best not to mislead people into thinking it does.
    for (i = 0, n = elements.length; i.>
        el = elements[i];
        el.addEventListener('click', something_wrapper(i, el), false);

  9. patriots jerseys, http://www.newenglandpatriotsjerseys.us/
    saints jerseys, http://www.neworleanssaintsjerseys.us/
    fitflop, http://www.fitflop.in.net/
    karen millen uk, http://www.karenmillendressesoutlets.co.uk/
    air jordan 11 free shipping, http://www.airjordan11.net/
    hollister canada, http://www.hollistercanada.com/
    hollister uk, http://www.hollistershirts.co.uk/
    tods outlet, http://www.todsoutlet.us.com/
    air jordan 4 free shipping, http://www.airjordan4.org/
    pandora jewelry, http://www.pandora.eu.com/
    coach outlet online, http://www.coachoutletstores.com.co/
    tiffany and co, http://www.tiffanyandco.in.net/
    prada outlet, http://www.pradaoutlet.us/
    ravens jerseys, http://www.baltimoreravensjerseys.us/
    supra shoes, http://www.suprashoes.us.com/
    hogan,hogan outlet,scarpe hogan,hogan sito ufficiale,hogan interactive
    michael kors outlet, http://michaelkors.outletonlinestores.us.com/
    juicy couture tracksuit, http://www.juicycoutureoutlet.net/
    chicago bulls, http://www.chicagobullsjerseys.net/
    jets jersey, http://www.newyorkjetsjersey.us/

  10. How could i get the sender and event type?

  11. Thanks u, i have tried and i got the answer. It is awesome :)

  12. The name is IIFE(Immediately Invoked Function Expressions)

  13. Il Chelsea 16-17 terzo kit dispone di una combinazione di colori tradizionale in bianco e blu. Realizzato da Adidas, la nuova terza maglia del Chelsea 2016-2017 è stato rilasciato il 29 luglio ed è impostato per funzionare come Maglia nelle Coppe.maglie calcio 2017,
    maglie calcio poco prezzo, Maglia Deportivo prezzo a poco prezzo
    Maglia real madrid 2017 prezzo

  14. This NFL Flag for Tennessee Titans is constructed of polyester, measures 3x5 feet, and has two metal grommets for attaching to our 6' aluminum flagpoles or any of our tailgate pole systems. The perimeter of our NFL Flag for Tennessee Titans is double stitched and the team logos are screen printed into the flag so they won't peel. Because of its large size, these flags are great to hang on any wall in your game room, sports room, garage.stars and stripes flags,
    football flags salebuy Cleveland Browns house divided flags
    Pittsburgh Steelers banners shop

  15. for beginners like me need a lot of reading and searching for information on various blogs. and articles that you share a very nice and inspires me .
    obat telat datang bulan
    obat aborsi
    cara menggugurkan kandungan
    obat telat bulan
    obat penggugur kandungan

  16. I was reading your blog, This is really informative, Please keep me more update from your blog.

  17. This is a very good article material and it is very useful for us all. thank you . cara menggugurkan kandungan

  18. يمكنك الان التواصل مع اكبر شركة تنظيف بالدمام والتي تتميز بانها من اكبر الشركات التي تتبع الي مؤسسة ابراج دبي الكبري لخدمات التنظيف جميعها عن طريق ارقام الخاصة لشركة التنظيف الكبري .

  19. This blog is a great source of information which is very useful for me.

    Jual Obat Aborsi Pekanbaru

    Jual Obat Aborsi Bekasi

    Jual Obat Aborsi malang

    Jual Obat Aborsi

    Obat Aborsi semarang

    Obat Aborsi yogyakarta

    Obat Aborsi solo

    Obat Aborsi

    -Can be very slow but shows all backlinks along with their PR, Anchor and if it's a Nofollow

  20. EFFING AWESOME! This was truly great find!

  21. Hi, Great.. Tutorial is just awesome..It is really helpful for a newbie like me.. I am a regular follower of your blog. Really very informative post you shared here. Kindly keep blogging. If anyone wants to become a Front end developer learn from Javascript Training in Chennai . or learn thru Javascript Training in Chennai. Nowadays JavaScript has tons of job opportunities on various vertical industry. JavaScript Training in Chennai

  22. Liquidating checks, for instance, is a considerable measure less demanding these days than it was previously. 26 hours check cashing Chicago