0

This has now been answered. Not an issue with ES6 or event listeners, but rather how innerHTML is working. It is removing all the html (breaking the listeners) and then adding the html back in with only the latest listener attached. Thank you.

Basically I am having issues with ES6 Classes and event listeners. Or it could very well just be my understanding of the whole thing.

I am dynamically creating 5 instances of the same class called 'Thing'. Each 'Thing' writes a piece of HTML to the DOM containing a button and adds an event listener to handle clicks. Elements are being selected using document.querySelector and based off a data attribute called 'indexNumber' to uniquely identify each button.

As each thing is its own instance I imagined that each button would work but only the last button does. Why is one instance of a class overwriting another?

Can anyone explain what is happening here?

HTML

<h1>Things</h1>
<span class="output"></span>
<div class="thingHolder"></div>

JS (Babel)

class Thing {
  constructor(index) {
    this.Index = index;
    this.html = '<div class="thing" data-index-number="'+ index +'">Thing ' + index + '<button type="button">Click Me!</button></div>';
    this.renderDom();
  }
  renderDom(){
    const thingHolder = document.querySelector(".thingHolder");
    thingHolder.innerHTML += this.html;

     this.addEventListeners();
  }

  addEventListeners(){
    const button = document.querySelector('.thingHolder .thing[data-index-number="' + this.Index + '"] button');

    button.addEventListener("click", () => {
      this.doSomething()
    }, false);
  }

  doSomething(){
    const output = document.querySelector(".output");
    output.innerHTML = 'You clicked thing #' + this.Index;
    console.log('You clicked thing #' + this.Index)
  }
}

for(var i = 0; i < 5; i++) {
 var thing = new Thing(i);
}
StueyKent
  • 347
  • 4
  • 14

2 Answers2

2

The problem has nothing to do with ES6 or JavaScript.

thingHolder.innerHTML += this.html;

will destroy and recreate all existing children of thingHolder. In that process, existing event handlers are destroyed.

Instead of creating HTML and using innerHTML you should be using the DOM API to create and append new element.

Example:

var div = document.createElement('div');
div.addEventListener('click', () => { ... });

// ...
thingHolder.appendChild(div);
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
1

When you use thingHolder.innerHTML += html the old objects in the container are removed and recreated, but without their event listeners. So you are recreating them all every time you insert a new node that way.

The fact that the first button is the only one that works is because you are attaching an event only to the first element you encounter inside the parent (with querySelector), just after you removed all other events.

So the solution would be to create your elements with JS (or whatever is used in Babel) instead of using innerHTML.

germanfr
  • 547
  • 5
  • 19