0

So I have the following code which doesn't recognise the buttons[i] variable inside the onclick function. Of course if I replace it with 'this', it works fine. (By 'works', I mean it functions as intended and changes the class of the body of the page.) My question is, if the array called 'buttons' is visible inside the anonymous function, why is buttons[i] not visible? Or is it the i that is not visible? I know that 'this' would always be used in such a case but I am just confused as to why buttons[i] does not also work.

(function(){
     var buttons = document.getElementsByTagName("button");
     for (var i =0, len = buttons.length; i < len; i++)
     {
         buttons[i].onclick = function(){
             debugger

             var className = buttons[i].innerHTML.toLowerCase();
            // console.log(className);
             document.body.className = className;
         }
     }



}());
  • 2
    Ah, the classic closures + loop problem... http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example – RobH Dec 13 '13 at 13:55
  • Thanks to everyone for the great and speedy answers! I don't have enough rep to vote up, sadly. – Richard Mansfield Dec 13 '13 at 14:13
  • Just btw, this is a great JS resource and includes this problem in its "Closures" section: http://ejohn.org/apps/learn/ – RobH Dec 13 '13 at 14:15

3 Answers3

1

Because by the time it's clicked, i == buttons.length, and buttons[i] is therefore undefined. There are many ways to deal with this, including binding the handler to the the right object, or introducing a closure that contains the right reference. One of the most backward-compatible ways is this:

for (var i =0, len = buttons.length; i < len; i++) {
    buttons[i].onclick = (function(button) {
        return function(){
            var className = button.innerHTML.toLowerCase();
            document.body.className = className;
        };
    }(buttons[i]));
 }
Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
0

You're having a problem with closures here. Because the loop will have executed before someone fires the click event your variable i = buttons.length which in this case would be five as there are five buttons. So when you're referencing buttons[i] you are in fact referencing buttons[5] which doesn't exist.

James
  • 2,195
  • 1
  • 19
  • 22
0

This is to do with javascript closures. This article explains closures quite well

buttons[i].onclick property takes in a function reference as an argument (in your case, you've passed in an anonymous function). The function that you are passing in however, is not in the same scope as your for loop. That means that you have to explicitly pass down any variables that are required inside that function. The variable button is available because you've passed that in to your anon function as a parameter.

dnshio
  • 914
  • 1
  • 8
  • 21