What am I doing wrong in scoping my function?

Tags: , ,



In this test case, I am using append.child with plain JavaScript to add 3 kinds of divs (blue, red, green) to a parent multiple times according to their corresponding button onclicks, then I am adding another child inside the added div with another button (innerButton).

My issue is that, the onclick function which is assigned to the innerbutton and is nested within the initial function, listens only to the very first appended div, and it adds the input (which is supposed to be added to the div I’m clicking on) to the last append element of its ‘kind’. I am doing something wrong with my scoping but I can’t see it. I just started studying JavaScript, so I am not familiar yet with libraries, jQuery etc.

var countBlue = 0;
var countRed = 0;
var countGreen = 0;

function addBlue() {
  var addTo = document.getElementById('div1')
  var blue = document.createElement("div");

  blue.id = "blueDiv";
  blue.innerHTML = "<input id=blueInput><button id=innerButtonBlue onclick=addInputs()>ADD INPUTS</button>";
  addTo.appendChild(blue);

  document.getElementById("innerButtonBlue").onclick = function() {
    var newInput = document.createElement("div");
    newInput.innerHTML = '<input id="newInput" placeholder="NEW">';
    blue.appendChild(newInput);
  }

  countBlue++;
}

function addRed() {
  var addTo = document.getElementById('div1')
  var red = document.createElement("div");

  red.id = "redDiv";
  red.innerHTML = "<input id=redInput><button id=innerButtonRed>ADD INPUTS</button>";
  addTo.appendChild(red);

  document.getElementById("innerButtonRed").onclick = function() {
    var newInput = document.createElement("div");
    newInput.innerHTML = '<input id="newInput" placeholder="NEW">';
    red.appendChild(newInput);
  }

  countRed++;

}

function addGreen() {
  var addTo = document.getElementById('div1')
  var green = document.createElement("div");

  green.id = "greenDiv";
  green.innerHTML = "<input id=greenInput><button id=innerButtonGreen>ADD INPUTS</button>";
  addTo.appendChild(green)

  document.getElementById("innerButtonGreen").onclick = function() {
    var newInput = document.createElement("div");
    newInput.innerHTML = '<input id="newInput" placeholder="NEW">';
    green.appendChild(newInput);
  }
  countGreen++;
}

function displayCounters() {
  alert("Blue divs amount : " + parseInt(countBlue) + "n" + " Red divs amount : " + parseInt(countRed) + "n" + " Green divs amount : " + parseInt(countGreen) + "n" + "n" + " All together is : " + (parseInt(countBlue) + parseInt(countRed) + parseInt(countGreen)))
}
button {
  margin-bottom: 10px;
}

#blueDiv {
  margin-top: 10px;
  margin-bottom: 10px;
  width: 300px;
}

#redDiv {
  margin-top: 10px;
  margin-bottom: 10px;
  width: 300px;
}

#greenDiv {
  margin-top: 10px;
  margin-bottom: 10px;
  width: 300px;
}

input {
  text-align: center;
}

#innerButtonRed {
  position: relative;
  float: right;
}

#innerButtonBlue {
  position: relative;
  float: right;
}

#innerButtonGreen {
  position: relative;
  float: right;
}

#newInput {
  margin-top: 2px;
  width: 162px;
  height: 23px;
}

#redInput {
  background: red;
}

#blueInput {
  background: blue;
}

#greenInput {
  background: green;
}
<html>

<body>
  <script src="test.js"></script>
  <link rel="stylesheet" type="text/css" href="test.css">
  <button onclick="addBlue()">BLUE</button>
  <button onclick="addRed()">RED</button>
  <button onclick="addGreen()">GREEN</button>
  <button onclick="displayCounters()">COUNTERS</button>
  <div id="div1"></div>
</body>

</html>

Answer

The first thing you need to know is that, although you can technically add the same id to multiple elements, it is bad practice to do so. The id of an element should be unique. If you need to apply the same style or target multiple elements with your code you should use class instead of id. I think that’s what is causing issues in your code.

Second, since you say you are learning, i think it would be good if you tried to make a single function to add the elements since the code is repeated in all of the three functions, except for the color. Try making the function accept the color as a variable so you can reuse it for the three colors. Imagine if it was a hundred colors.

var countBlue = 0;
var countRed = 0;
var countGreen = 0;

function addBlue() {
  var addTo = document.getElementById('div1')
  var div = document.createElement("div");

  countBlue++; //set the counter to one so ids don't start at zero
  
  div.id = `blueDiv-${countBlue}`; //creates a unique id depending on the counter
  div.classList = "blueDiv";
  div.innerHTML = `<input id="blueInput-${countBlue}" class="blueInput"><button id="innerButtonBlue-${countBlue}" onclick="addInputs">ADD INPUTS</button>`;
  addTo.appendChild(div);

  document.getElementById(`innerButtonBlue-${countBlue}`).onclick = function() {
    var newInput = document.createElement("div");
    newInput.innerHTML = `<input id="newInput-blue-${countBlue}" class="newInput" placeholder="NEW">`;
    div.appendChild(newInput);
  }
}

function addRed() {
  var addTo = document.getElementById('div1')
  var div = document.createElement("div");

  countRed++
  
  div.id = `redDiv-${countRed}`;
  div.classList = "redDiv";
  div.innerHTML = `<input id="redInput-${countRed}" class="redInput"><button id="innerButtonRed-${countRed}" onclick="addInputs">ADD INPUTS</button>`;
  addTo.appendChild(div);

  document.getElementById(`innerButtonRed-${countRed}`).onclick = function() {
    var newInput = document.createElement("div");
    newInput.innerHTML = `<input id="newInput-red-${countRed}" class="newInput" placeholder="NEW">`;
    div.appendChild(newInput);
  }
}

function addGreen() {
  var addTo = document.getElementById('div1')
  var div = document.createElement("div");

  countGreen++
  
  div.id = `greenDiv-${countGreen}`; 
  div.classList = "greenDiv";
  div.innerHTML = `<input id="greenInput-${countGreen}" class="greenInput"><button id="innerButtonGreen-${countGreen}" onclick="addInputs">ADD INPUTS</button>`;
  addTo.appendChild(div);

  document.getElementById(`innerButtonGreen-${countGreen}`).onclick = function() {
    var newInput = document.createElement("div");
    newInput.innerHTML = `<input id="newInput-green-${countGreen}" class="newInput" placeholder="NEW">`;
    div.appendChild(newInput);
  }
}

function displayCounters() {
  alert("Blue divs amount : " + parseInt(countBlue) + "n" + " Red divs amount : " + parseInt(countRed) + "n" + " Green divs amount : " + parseInt(countGreen) + "n" + "n" + " All together is : " + (parseInt(countBlue) + parseInt(countRed) + parseInt(countGreen)))
}
button {
  margin-bottom: 10px;
}

.blueDiv {
  margin-top: 10px;
  margin-bottom: 10px;
  width: 300px;
}
.redDiv {
  margin-top: 10px;
  margin-bottom: 10px;
  width: 300px;
}
.greenDiv {
  margin-top: 10px;
  margin-bottom: 10px;
  width: 300px;
}

input {
  text-align: center;
}

.innerButtonRed {
  position: relative;
  float: right;
}

.innerButtonBlue {
  position: relative;
  float: right;
}

.innerButtonGreen {
  position: relative;
  float: right;
}

.newInput {
  margin-top: 2px;
  width: 162px;
  height: 23px;
}

.redInput {
  background: red;
}

.blueInput {
  background: blue;
}

.greenInput {
  background: green;
}
<button onclick="addBlue()">BLUE</button>
<button onclick="addRed()">RED</button>
<button onclick="addGreen()">GREEN</button>
<button onclick="displayCounters()">COUNTERS</button>
<div id="div1"></div>


Source: stackoverflow