1

How do I return the index of only the button that is clicked?

Here's the code that got me worried:

window.onload = function(){

var    description = document.getElementsByClassName('description'),
        buttons = document.getElementsByClassName('button');

var currD = 0; // this var stands for the current description that should be shown

var show = function(){

    for( var i = 0; i < description.length; i++ ){

        if( i !== currD ){
            description[i].style.display='none';
        }

        else if( i === currD ){
            description[i].style.display='block';   
        }

    }

};

  for (var i = 0; i < buttons.length; i++){
    buttons[i].addEventListener('click', function(){
        currD = i;
        console.log(i);
    });
}

    window.setInterval(show,300);
};

Every time I click the button the for loop return the last element.

Since I didn't have many buttons on this page I went for the unproficient old way which is:

window.onload = function(){

var description = document.getElementsByClassName('description'),
    buttons = document.getElementsByClassName('button');

    var currD = 0; // this var stands for the current description that should be shown

    var show = function(){

        for( var i = 0; i < description.length; i++ ){

            if( i !== currD ){
                description[i].style.display='none';
            }

            else if( i === currD ){
                description[i].style.display='block';   
            }

        }

    };

buttons[0].addEventListener('click', function(){
    currD = 0;
});

buttons[1].addEventListener('click', function(){
    currD = 1;
});

buttons[2].addEventListener('click', function(){
    currD = 2;
});

        window.setInterval(show,300);
    };

This works but if I want to add more buttons it'd be a loss of time setting all the event listeners.

Community
  • 1
  • 1
  • 1
    Possible duplicate of [JavaScript closure inside loops – simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – Teemu Jan 19 '16 at 10:29

1 Answers1

1

This is a common CLOSURE issue. This should work:

for (var i = 0; i < buttons.length; i++){

    (function (_i) {
        buttons[_i].addEventListener('click', function(){
          currD = _i;
          console.log(_i);
        });
    })(i);

}

Another solution:

for (var i = 0; i < buttons.length; i++){

    buttons[i].addEventListener('click', function(){
        return function() {
            currD = i;
            console.log(i);
        }
    });
}

You can read more about closure even here: http://www.w3schools.com/js/js_function_closures.asp but there are a lot of other interesting articles. Just google javascript closure

For your specific example, maybe even something like this can work:

for (var i = 0; i < buttons.length; i++){
    buttons[i].addEventListener('click', function(e){
        currD = buttons.indexOf( e.currentTarget );
        console.log(currD);
    });
}
  • The first solution is awful, the second solution doesn't work : (. – Teemu Jan 19 '16 at 10:39
  • Lol. Those are the solutions for closure. Why a function wrapper is awful? Anyway, can you create a jsFiddle so we can show it working? – lucian.nicolaescu Jan 19 '16 at 10:42
  • In the first solution you're creating a new function on every round, that works though, but is considered a bad practice. In the second snippet `i` inside of the handler function is the event object, not the looping variable. – Teemu Jan 19 '16 at 10:44
  • Also, the second solution does the same thing as the first one. Yes, that's the idea of the closure, you need to create a function for each element. Otherwise you will not have the i variable!. You need to set different functions for each button with the corresponding "i". – lucian.nicolaescu Jan 19 '16 at 10:46
  • Actually the second solution [doesn't work at all](https://jsfiddle.net/wekftz8t/). – Teemu Jan 19 '16 at 10:51
  • Thank you very much for your answer it works indeed even though it got me pondering how, I'll search some information about it. – il Leone Ami Jan 19 '16 at 10:59
  • At the end I decided to use this code: // create an array that contains the functions to add to the event listener var buttonFunctions = []; // create the function of each button function buttonF(i){ return function(){ currD = i; } } // add the above functions to the array for (var i = 0; i < buttons.length; i++){ buttonFunctions[i] = buttonF(i); } // add the event listeners for (var g = 0; g < buttons.length; g++){ buttons[g].addEventListener('click',buttonFunctions[g]); } – il Leone Ami Jan 19 '16 at 11:37
  • the solution I don't understand at all was the fist one you wrote, specifically how does the program read this: (function(_i){ /* code */ })(i)?? – il Leone Ami Jan 19 '16 at 11:44
  • You can read more about that here: http://stackoverflow.com/a/2421949/5794995 and you can also check the jQuery example here: http://stackoverflow.com/a/1639214/5794995 – lucian.nicolaescu Jan 19 '16 at 11:50