0

Im trying to dinamically insert names into the value of an input element. I try to pass players[i] as an argument for the function addPlayer but it says undefined. When I click on each name it should be passing it to the input field. Where am I failing?

var players = ["john","doe"];

function addPlayer(str) {
    console.log(str);
    document.getElementsByClassName("answerInput")[0].value += str;
}


for (var i = 0; i < players.length; i++) {
    var node = document.createElement("div");
    var textnode = document.createTextNode(players[i]);
    node.appendChild(textnode);
    node.classList.add("newPlayers");
    document.getElementById("pushPlayer").appendChild(node);
    node.addEventListener('click', function () {
           addPlayer(players[i]);
        }, false);
};
N.Car
  • 492
  • 1
  • 4
  • 14
  • You have a closure around the `i` variable, which means that when any node gets clicked, `i` will be the last value the loop assigned to it and all nodes will share that value. – Scott Marcus Mar 25 '18 at 01:14

1 Answers1

1

The problem is the variable i declared with the keyword var

The scope of a variable declared with var is its current execution context, so, the variable i will end the for-loop with the last value.

var players = ["john (click me)", "doe (click me)"];

for (var i = 0; i < players.length; i++) {
  var node = document.createElement("div");
  var textnode = document.createTextNode(players[i]);
  node.appendChild(textnode);
  node.classList.add("newPlayers");
  document.getElementById("pushPlayer").appendChild(node);
  node.addEventListener('click', function() {
      console.log(i)
  }, false);
};
<div id='pushPlayer'>
</div>

An alternative is declaring that variable with the statement let

Variables declared by let have as their scope the block in which they are defined, as well as in any contained sub-blocks. In this way, let works very much like var. The main difference is that the scope of a var variable is the entire enclosing function.

var players = ["john", "doe"];

function addPlayer(str) {
  console.log(str);
  document.getElementsByClassName("answerInput")[0].value += str;
}

for (let i = 0; i < players.length; i++) {
  var node = document.createElement("div");
  var textnode = document.createTextNode(players[i]);
  node.appendChild(textnode);
  node.classList.add("newPlayers");
  document.getElementById("pushPlayer").appendChild(node);
  node.addEventListener('click', function() {
      addPlayer(players[i]);
  }, false);
};
<input class='answerInput' placeholder='Enter answer'>
<div id='pushPlayer'>
</div>

Another alternative is using IIFE to return a function with a specific value/player.

var players = ["john", "doe"];

function addPlayer(str) {
  console.log(str);
  document.getElementsByClassName("answerInput")[0].value += str;
}

for (var i = 0; i < players.length; i++) {
  var node = document.createElement("div");
  var textnode = document.createTextNode(players[i]);
  node.appendChild(textnode);
  node.classList.add("newPlayers");
  document.getElementById("pushPlayer").appendChild(node);
  node.addEventListener('click', (function(player) {
    return function () {
      addPlayer(player);
    }
  })(players[i]), false);
};
<input class='answerInput' placeholder='Enter answer'>
<div id='pushPlayer'>
</div>
Ele
  • 33,468
  • 7
  • 37
  • 75