ThomasG2201
ThomasG2201

Reputation: 877

Cannot use addEventListener() on a createElement() (a button) after displaying it in the DOM (in javascript)

I am creating a Javascript framework recently and I am facing two problems that I don't understand at all. When I create a new button with createElement() in Javascript, I can't place it inside several elements at the same time without killing the "click" event (if this is an id, it works).

(Note : "button.onclick = function()" doesn't work either)

Here is the code : (I don't want to use any libraries)

function createButton() {
    var button = document.createElement("button");
    button.innerHTML = "YOOOO";
    button.style.backgroundColor = "red";
    button.style.color = "white";

    // Problem Two : If I click on the button, nothing happends
    button.addEventListener("click", function(e) {
        alert(e.target);
    }, false);

    var clas = document.getElementsByClassName("test");
    Array.from(clas).forEach(element => {
        element.appendChild(button);
        element.innerHTML += ""; // First problem : If I don't do this, the button does not appear in the first div. I have no idea why !
    });
}
createButton();
 body {
    margin: 0;
    padding: 0;
    display:flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    background-color: rgba(30, 30, 30, 0.2);
}

button {
    cursor: pointer;
    width: 80px;
    height: 50px;
    padding: 10px;
}
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>bug</title>
</head>

<body>

<div class="test" id="1" style="padding: 50px;">
    <span>1</span>
</div>
<div class="test" id="2" style="padding: 50px;">
    <span>2</span>
</div>

</body>
</html>

Upvotes: 0

Views: 2677

Answers (3)

mplungjan
mplungjan

Reputation: 178413

I strongly suggest you delegate and clone

I think I already posted this answer for you a few days ago

function createButton() {
  const button = document.createElement("button");
  button.classList.add("redwhite");
  button.innerHTML = "YOOOO";
  [...document.querySelectorAll(".test")].forEach(element => {
    element.appendChild(button.cloneNode(true));
  });
}
createButton();
document.getElementById("container").addEventListener("click", function(e) {
  const tgt = e.target;
  if (tgt.classList.contains("redwhite")) {
    console.log(tgt.closest("div").id)
  }  
})
body {
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-color: rgba(30, 30, 30, 0.2);
}

button {
  cursor: pointer;
  width: 80px;
  height: 50px;
  padding: 10px;
}

.redwhite {
  background-color: red;
  color: white;
}
<div id="container">
  <div class="test" id="1" style="padding: 50px;">
    <span>1</span>
  </div>
  <div class="test" id="2" style="padding: 50px;">
    <span>2</span>
  </div>
</div>

Upvotes: 0

connexo
connexo

Reputation: 56813

Your button is a single instance, and repeatedly calling

element.appendChild(button);

just moves it in the DOM repeatedly.

Your subsequent call to

element.innerHTML += "";

removes that reference from the DOM and instead creates a new button from its outerHTML. That is also where your added event listener gets lost.

If you need more than one button, your also need to create more than one button.

Upvotes: 0

CertainPerformance
CertainPerformance

Reputation: 371168

There are 2 problems:

  • element.innerHTML += "";, where element refers to the button's parent, will force the parent to re-parse its contents from their HTML string, thus corrupting any existing handlers on child elements. The element the listener is attached to will be removed from the DOM and replaced with a different one; the new one does not have the listener.

  • When you do appendChild, if the element being appended already exists in the DOM, it'll be removed from where it existed previously before being appended to its new location. Since you only do document.createElement('button') once, without the innerHTML += '', the button gets removed from its first position when appended to the second.

Create the button inside the loop instead, and both problems are solved.

function createButton() {
  var clas = document.getElementsByClassName("test");
  Array.from(clas).forEach(element => {

    var button = document.createElement("button");
    button.innerHTML = "YOOOO";
    button.style.backgroundColor = "red";
    button.style.color = "white";

    // Problem Two : If I click on the button, nothing happends
    button.addEventListener("click", function(e) {
      alert(e.target);
    }, false);
    element.appendChild(button);

  });
}
createButton();
body {
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-color: rgba(30, 30, 30, 0.2);
}

button {
  cursor: pointer;
  width: 80px;
  height: 50px;
  padding: 10px;
}
<div class="test" id="1" style="padding: 50px;">
  <span>1</span>
</div>
<div class="test" id="2" style="padding: 50px;">
  <span>2</span>
</div>

Or, making the code a bit prettier:

function createButton() {
  for (const parent of document.querySelectorAll('.test')) {
    const button = parent.appendChild(document.createElement("button"));
    button.textContent = "YOOOO";
    button.style.backgroundColor = "red";
    button.style.color = "white";
    button.addEventListener("click", function(e) {
      console.log(e.target);
    });
  }
}
createButton();
body {
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-color: rgba(30, 30, 30, 0.2);
}

button {
  cursor: pointer;
  width: 80px;
  height: 50px;
  padding: 10px;
}
<div class="test" id="1" style="padding: 50px;">
  <span>1</span>
</div>
<div class="test" id="2" style="padding: 50px;">
  <span>2</span>
</div>

Upvotes: 3

Related Questions