skip to Main Content

I’m trying to add an eventlistener for a button collection inside of a for loop, but I’m kind of lost because in following example the eventlistener only works for the last button in the collection, and my country in addCountry function is undefined. I would appreciate any kind of feedback or help.

I have tried to console.log(i) and obviously it is working as it should. And I have console.log('.add'[i]) and that is displaying the button element so I don’t understand how this works really.

let countries = ["Sweden", "Norway", "Denmark", ]

const main = document.getElementById("maincontent")
const cart = document.getElementById('cart')

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

  addContent()

  document.querySelectorAll('.add')[i].addEventListener('click', addCountry)
}

function addContent() {
  main.innerHTML += `<div>
  <h1>${countries[i]}</h1>
  <div class="one hide">
      <p>Some text</p>
      <button class="add">Add</button>
  </div>
  </div>`
}

function addCountry() {
  cart.innerHTML = `<div>
<h1>${countries[i]}</h1>
<div class="one hide">
    <p>Some text</p>
    <button class="delete">Delete</button>
</div>
</div>`
}
<main id="maincontent">

</main>
<div id="cart">

</div>

4

Answers


  1. You can’t use i in addCountry. It’s a global variable, not closed over the loop iteration.

    You need to put a closure into the loop, and then pass i as an argument to addCountry().

    let countries = ["Sweden", "Norway", "Denmark", ]
    
    const main = document.getElementById("maincontent")
    const cart = document.getElementById('cart')
    
    for (let i = 0; i < countries.length; i++) {
      addContent(i)
      document.querySelectorAll('.add')[i].addEventListener('click', () => addCountry(i))
    }
    
    function addContent(i){
      main.innerHTML+=`<div>
      <h1>${countries[i]}</h1>
      <div class="one hide">
          <p>Some text</p>
          <button class="add">Add</button>
      </div>
      </div>`
    }
    function addCountry(i) {
      cart.innerHTML = `<div>
    <h1>${countries[i]}</h1>
    <div class="one hide">
        <p>Some text</p>
        <button class="delete">Delete</button>
    </div>
    </div>`
    }
    Login or Signup to reply.
  2. Using event delegation and data attributes you can avoid having to add multiple event handlers.

    let countries = ["Sweden", "Norway", "Denmark", ]
    
    const main = document.getElementById("maincontent")
    const cart = document.getElementById('cart')
    
    const html = countries.map(addContent);
    main.innerHTML = html.join('');
    
    main.addEventListener("click", function (e) {
      const btn = e.target.closest('[data-country]');
      if (!btn) return;
      const country = btn.dataset.country;
      addCountry(country);
    });
    
    cart.addEventListener("click", function (e) {
      const btn = e.target.closest('.delete');
      if (!btn) return;
      btn.closest(".wrapper").remove();
    });
    
    
    function addContent(country) {
      return `
      <div>
        <h1>${country}</h1>
        <div class="one hide">
          <p>Some text</p>
          <button class="add" data-country="${country}">Add</button>
        </div>
      </div>`;
    }
    
    function addCountry(country) {
      cart.innerHTML += `
      <div class="wrapper">
        <h1>${country}</h1>
        <div class="one hide">
          <p>Some text</p>
          <button class="delete">Delete</button>
        </div>
      </div>`;
    }
    <main id="maincontent"></main>
    <div id="cart"></div>
    Login or Signup to reply.
  3. You are doing a small mistake at event binding. Update this line of the code:-

    for (i = 0; i < countries.length; i++) {
      addContent();
    
       document.querySelectorAll('.add').forEach((item)=>{
         item.addEventListener('click', addCountry);
       });
    }
    
    Login or Signup to reply.
  4. Here’s another approach it seems easy to understand but don’t hesitate if you have any question.

    This solution avoid to always use innerHTML!

    I don’t understand if you want to duplicate nodes or just create a document as specified and add a listener to it.
    in the clickHandler() method you can easily do what you want…
    Duplicate an Object, add the destination to a cart…

    If you want to see the logs, click on "Full page" for the snippet.

    Best regards.

    let countries = ["Sweden", "Norway", "Denmark"];
        function addElements(){
            for(var i=0; i<countries.length; i++){
                constructElement(i)
            }
        }
        function constructElement(i){
            var newDiv = document.createElement("div");
            var newH1 = document.createElement("h1");
            var newTitle = document.createTextNode(countries[i]);
            var innerDiv = document.createElement("div");
            innerDiv.classList.add("inDiv");
            var paragraph = document.createElement("p");
            var btn = document.createElement("button");
            btn.setAttribute("value", i);
            btn.classList.add("inButton");
            var newText = document.createTextNode("Country n°" + (i+1) + " :");
            var buttonText = document.createTextNode(countries[i]);
            newDiv.appendChild(newH1);
            newH1.appendChild(newTitle);
            newDiv.appendChild(innerDiv)
            innerDiv.appendChild(paragraph);
            paragraph.appendChild(newText);
            innerDiv.appendChild(btn);
            btn.appendChild(buttonText)
            document.body.appendChild(newDiv);
            btn.addEventListener("click", clickEvent);
        }
        addElements();
        function clickEvent(event){
            console.log("button clicked value = " + this.value + ", country = " + this.textContent);
            // then you may add conditions related to the button value...
            if(this.value == 0){
                console.log("Do you live in " + countries[this.value] + "?");
            }else if(this.value == 1){
                console.log("Are you happy in " + countries[this.value] + "?");  
            }else if(this.value == 2){
                console.log("Are you really leaving " + countries[this.value] + "?");    
            }
        }
            html, body{
                font-family: sans-serif;
            }
            h1{
                font-size: 1.2em;
            }
            p{
                margin:0;
            }
            .inDiv{
                padding:10px;
                color:#009900;
                border:1px solid #000000;
                width:300px;
                font-weight:bold;
            }
            .inButton{
                padding:5px;
                color:#990000;
                width:100px;
            }
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search