Event listeners don't get re-attached to my HTML elements when using ES6 without re-feshing the page

Viewed 62

I am printing a simple string to the screen. When clicking on one of its letters, is should be removed from wherever it is the string and added at the end. After I click on one letter and the new string is getting printed to the page, the letters don't preserve their event listeners. This is the JS code and here is all the code https://codesandbox.io/s/reverse-array-forked-fvclg?file=/src/index.js:0-1316:

const appBox = document.getElementById("app");

const convertString = (string) => {
  let stringToArray = string.split("");
  return stringToArray;
};

let stringToArray = convertString("Hello world!");

const printArrayToPage = (string) => {
  string.forEach((element) => {
    const textBox = document.createElement("div");

    if (element !== " ") {
      textBox.classList.add("letter");
    } else {
      textBox.classList.add("emptySpace");
    }

    const text = document.createTextNode(element);
    textBox.appendChild(text);
    appBox.appendChild(textBox);
  });
};

window.onload = printArrayToPage(stringToArray);

const moveLetter = (event) => {
  const targetLetter = event.target;
  const letterToRemove = targetLetter.innerHTML;
  targetLetter.classList.add("invisible");

  if (stringToArray.includes(letterToRemove)) {
    const index = stringToArray.indexOf(letterToRemove);
    stringToArray.splice(index, 1);
  }

  stringToArray.push(letterToRemove);
  appBox.innerHTML = "";
  printArrayToPage(stringToArray);
};

const allLetters = document.querySelectorAll(".letter");
allLetters.forEach((element) => element.addEventListener("click", moveLetter));
const allSpaces = document.querySelectorAll(".emptySpace");
allSpaces.forEach((element) => element.addEventListener("click", moveLetter));

I tried moving the even assignments (this block)

const allLetters = document.querySelectorAll(".letter");
allLetters.forEach((element) => element.addEventListener("click", moveLetter));
const allSpaces = document.querySelectorAll(".emptySpace");
allSpaces.forEach((element) => element.addEventListener("click", moveLetter));

inside printArrayToPage function but because I am using ES6 syntax, I can't use a function before its definition. If I change to functions created used the function keyword, everything works as expected. How can I fix this issue using ES6 so that after I click on a letter and the divs get re-added to the page, event listeners are re-assigned?

2 Answers

If you want to move the letter node to the end, you can use the Node.parentNode property, and append the child node to the end. You don't need to create the nodes every time an item is clicked:

const moveLetter = (event) => {
  const targetLetter = event.target;
  const parent = targetLetter.parentNode;
  parent.appendChild(targetLetter); // move the element to the end
};

See: https://codesandbox.io/s/reverse-array-forked-lf333?file=/src/index.js

Use event delegation. Here's a simple example snippet:

// adding an event listener to the document
document.addEventListener("click", evt => {
  console.clear();
  const origin = evt.target;
  //             ^ where did the event originated?
  if (origin.closest("#bttn1")) {
    console.log("you clicked button#bttn1");
  } else if (origin.closest("#bttn2")) {
    console.log("you clicked button#bttn2")
  }
});

createElement("h3", "header", "Wait 2 seconds...");

// creating buttons after a while. The earlier created
// event listener will detect clicks for the new buttons
setTimeout( () => {
    createElement("button", "bttn1", "click me");
    createElement("button", "bttn2", "click me too");
  }, 2000);

function createElement(nodetype, id, text) { 
  document.body.appendChild(
    Object.assign(document.createElement(nodetype), {id, textContent: text})
  );
}
body {
  font: normal 12px/15px verdana, arial;
  margin: 2rem;
}
button {
  margin: 0 0.6rem 0 0;
}

Related