Search code examples
javascripthtmliconslistener

Some of Icon click listeners work and some not in a strange way in JavaScript


I am working on a to-do list application. I create a li item and put 2 icons and a p tag in it. One of the icons is edit and it works quite well, I replace an input with the p tag and it is fine but the problem is that my check icons on the left side work half way. If I add the li items one by one, the check icons work very well but when I add 5 or 10 items and then try to check the icons, a few of them works and the others do not. I have tried replacing i tags with span tags and no result. It is like every second li tag blocks the former one. I need help, I would appreciate any. I'll add below the only the icons which don't work.

const DONE = document.getElementsByClassName('far fa-circle');
const LINE = document.getElementsByClassName('list-points');
const EDIT = document.getElementsByClassName('far fa-edit');
const CONTAINER = document.getElementById("actual-container");
const BUTTON = document.getElementById("list-adder");
BUTTON.addEventListener('click', nameList);

function nameList() {
  const item1 = document.createElement("i");
  item1.className = "far fa-circle";
  const paraph1 = document.createElement("p");
  paraph1.className = "list-points";
  paraph1.innerText = "Fresh again!";
  const item2 = document.createElement("i");
  item2.className = "far fa-edit";
  const myList = document.createElement("li");
  myList.appendChild(item1);
  myList.appendChild(paraph1);
  myList.appendChild(item2);
  CONTAINER.appendChild(myList);
  for (let i = 0; i < DONE.length; i++) {
    DONE[i].addEventListener('click', function() {
      DONE[i].classList.toggle('fa-times-circle');
    })
  }
}
<head>
  <title>Debug</title>

  <script src="https://kit.fontawesome.com/ae444f90db.js" crossorigin="anonymous"></script>
</head>

<body>

  <div>
    <ul id="actual-container"></ul>
  </div>

  <button id="list-adder">ME</button>

</body>


Solution

  • The error is within the assignment of your click-handlers.

    You do

    for (let i = 0; i < DONE.length; i++) {
        DONE[i].addEventListener('click', function() {
            DONE[i].classList.toggle('fa-times-circle');
        });
    }
    

    This will add a toggling event handler to all elements that you keep in DONE and has this behaviour.

    1. Click button: create elements A, assign click handler to A
      • (A has a single handler) works
    2. Click button: create elements B, assign click handler to A and B
      • (A has two handlers) does not work
      • (B has a single handler) works
    3. Click button: create elements C, assign click handler to A and B and C
      • (A has three handlers) works
      • (B has two handlers) does not work
      • (C has a single handler) works

    Because you are using toggle in your click handler, the handlers are "canceling" each other because toggling something twice will leave you in the initial state.

    I guess you are not aware of the fact that getElementByClassName returns a live list, so that the elements of your variable DONE are changed when you add new elements to the DOM. I was not aware of this either, so thank you for your question :) See here for a better explanation: https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName

    In your code it should be enough to add the handler just to the one element you create (item1 is the icon element in your code):

    item1.addEventListener('click', function() {
        item1.classList.toggle('fa-times-circle');
    });