0

I want to attach the "click" event to all button inside a div. Except this:

var div1 = document.getElementById("div1");
var elements = div1.getElementsByTagName('button');
for(var i = 0, len = elements.length; i < len; i++) {
    elements[i].onclick = function () {
        // stuff
    }
}

Is there a better way to attach "onclick" function handler to all the buttons at once in pure js?

Karim
  • 31
  • 1
  • 7
  • 1
    You can use [Event Delegation](http://stackoverflow.com/questions/1687296/what-is-dom-event-delegation). – Mohammad Usman Dec 30 '16 at 15:56
  • You could always use `elements[i].addEventListener("click", functionName, false);` You could also change your `for` loop to `(var i=0; i < elements.length; i++)` – NewToJS Dec 30 '16 at 16:00
  • Bind one handler to the parent element. See Muhammads link. – Shilly Dec 30 '16 at 16:01

2 Answers2

2

As mentioned in the comments, you can use event delegation. In that case, a single event handler is added to a common ancestor of the elements you want to handle. Inside the handler, the element where the event originated is inspected and if it is one of the ones that should be handled, the actual event handler logic is executed.

In your case it could be like:

var div1 = document.getElementById('div1');
div1.onclick = function(event) {
  if (event.target.nodeName.toLowerCase() !== 'button') {
    return;
  }
  // stuff
};

Note that there differences in the event system between browsers, especially older IE versions. You will have to deal with that if want to support those versions. You might also want to consider using addEventListener instead of onclick.

Community
  • 1
  • 1
Felix Kling
  • 705,106
  • 160
  • 1,004
  • 1,072
  • you could also point the OP to [the MDN page for Event.target](https://developer.mozilla.org/en-US/docs/Web/API/Event/target) which discusses event delegation and has notes about compatibility (e.g. with IE 6-8) – Sᴀᴍ Onᴇᴌᴀ Dec 30 '16 at 19:04
0

In order to reduce the code you can define a clickHandler function and attach this function to all buttons.

The result of div1.getElementsByTagName('button') is an HTMLCollection and not an array. In order to take advantage of Array.forEach syntax you can use call.

The example:

var clickHandler = function (e) {
  console.log(this.textContent);
};

var div1 = document.getElementById("div1");
var elements = div1.getElementsByTagName('button');
Array.prototype.forEach.call(elements, function(ele) {
  ele.onclick = clickHandler
});
<div id="div1">
    <button type="button">Button 1</button>
    <button type="button">Button 2</button>
    <button type="button">Button 3</button>
    <button type="button">Button 4</button>
</div>

If you can use the Arrow Functions, they are supported only in ES6, you can compact more your code:

var clickHandler = function (e) {
  console.log(this.textContent);
};

var div1 = document.getElementById("div1");
var elements = div1.getElementsByTagName('button');
Array.prototype.forEach.call(elements, (ele) => {ele.onclick = clickHandler;});
<div id="div1">
    <button type="button">Button 1</button>
    <button type="button">Button 2</button>
    <button type="button">Button 3</button>
    <button type="button">Button 4</button>
</div>
gaetanoM
  • 39,803
  • 6
  • 34
  • 52
  • Your answer gives the impression that it is somehow important to use an arrow function here. Since it is not, could you demphazise the arrow function part? It also would be more call `forEach` instead of `.map`. – Felix Kling Dec 30 '16 at 16:24