Carmen Sirgo
Carmen Sirgo

Reputation: 33

How can I add click events to an array of buttons with their position?

I'm trying to add an event that changes the text of a button when I click on it. However it doesn't matter which one I click, it always changes the text of the last button.

At first I tried by passing the button to the method "pulsar", and always the last button was the one changing. I thought it may be the reference of the variable so I tried passing the possition, but the same happens.

class Buscaminas{
    constructor () {
        this.botones = [];
        for (var i = 0; i < 8; i++) {
            for (var j = 0; j < 7; j++) {
                let boton = document.getElementById(i + "" + j);
                boton.textContent = "Hi";
                boton.onclick = e => {
                    this.pulsar(6*i+j);
                }
                this.botones.push(boton);
            }
        }
    }

    pulsar = (n) => {
        console.log(n);
        this.botones[n].textContent = "Bye";
    }
}

The console.log always prints 55

Upvotes: 0

Views: 334

Answers (2)

user12005282
user12005282

Reputation:

You could try this:

function sayHello(e) {
  e.target.textContent = "Darth Vader";
}

var buttons = document.querySelectorAll("button");
buttons.forEach(e => e.addEventListener("click", sayHello, false));
<button>Hello</button>
<button>I</button>
<button>Am</button>
<button>Your</button>
<button>Father</button>

Upvotes: 1

Geuis
Geuis

Reputation: 42317

You're approaching this incorrectly.

You never want to add a bunch of individual event handlers to each element in a case where you have lots of elements. You want to create a single event handler at a root node where click events for each child element bubble up to.

In this example, we have 10 divs:

<div id="btn-root">
  <input type="button" class="btn" value="1">
  <input type="button" class="btn" value="2">
  <input type="button" class="btn" value="3">
  <input type="button" class="btn" value="4">
  <input type="button" class="btn" value="5">
  <input type="button" class="btn" value="6">
  <input type="button" class="btn" value="7">
  <input type="button" class="btn" value="8">
  <input type="button" class="btn" value="9">
  <input type="button" class="btn" value="10">
</div>

And we have this simple code that listens for clicks on any of the child ".btn" elements:

class Buscaminas {
  constructor() {
    document.querySelector('#btn-root').addEventListener('click', (evt) => {
      evt.target.textContent = Math.random();
    }); 
  }
}

new Buscaminas();

If you run that, you'll see that every time you click one of the buttons, its content is replaced with a random number.

Later edit:

Say you're in the case where you want to find the index of the element being clicked. We still don't want to assign individual event handlers for each element, but we aren't provided with the index of the target element in the parent.

To get around this, we want to search through the child nodes of #btn-root until it matches. A binary search won't work here, but we want something better than indexOf which just does a linear search. What we can do instead is only loop for the length of half the array and check both ends. Then we can get the index fairly efficiently.

document.querySelector('#btn-root').addEventListener('click', (ev) => {
  const children = ev.target.parentNode.children;

  for (let i = 0; i < Math.floor(children.length / 2); i++) {
    const lowMatch = ev.target === children[i] && i;
    const highMatch = ev.target === children[children.length - 1 - i] && children.length - 1 - i;
    const index = Number.isInteger(lowMatch) && lowMatch || Number.isInteger(highMatch) && highMatch;

    if (index) {
      ev.target.value = `${index + 1}-${Math.random()}`;
      break;      
    }
  }
});

Upvotes: 1

Related Questions