AddEventListener on all created li elements
Asked Answered
C

7

7

So I have a simple script that adds "li" elements to the "ul" and assigns them a class. Now I want to change the class of "li" item on click event.

Here is the HTML:

<form class="form">
<input id="newInput" type="text" placeholder="Dodaj pozycję">
<button id="createNew" type="button">Dodaj</button>
</form>
<h2>Moja lista:</h2>
<div class="listBg">
<ul id="list">
</ul>
</div>
<button id="deleteAll" type="button">Wyczyść</button>

And JS:

function addItem() {
    var myList = document.getElementById("list"); // get the main list ("ul")
    var newListItem = document.createElement("li"); //create a new "li" element
    var itemText = document.getElementById("newInput").value; //read the input value from #newInput
    var listText = document.createTextNode(itemText); //create text node with calue from input
    newListItem.appendChild(listText); //add text node to new "li" element
    if (itemText === "") { // if input calue is empty
        alert("Pole nie może być puste"); // show this alert
    } else { // if it's not empty
        var x = document.createElement("span"); // create a new "span" element
        x.innerText = "X"; // add inner text to "span" element
        x.className = "closer"; // add class to "span" element
        myList.appendChild(newListItem); // add created "li" element to "ul"
        newListItem.className = "item"; // add class to new "li" element
        newListItem.appendChild(x); // add a "span" to new "li" element
        var itemText = document.getElementById("newInput"); // read current input value
        itemText.value = ""; // set current input calue to null
    }
};

I was thinking something like this should do the trick, but it's not working:

function itemDone() {
    var listItems = document.querySelectorAll("li");
    var i;
    for (i = 0; i < listItems.length; i++) {
    listItem[i].className = "itemDone";
};
};

var item = document.getElementsByClassName("item");
item.addEventListener("click", itemDone);

I'm fairly new to javascript so I would appreciate some explanation with the answer.

Cristacristabel answered 8/2, 2017 at 11:27 Comment(2)
FWIW al these // random comment tend to obfuscate vs enhance clarity. Good names often are better than excess comments. For sample // add class to new "li" element this really does not add but "sets" the class since you would "add" to the classList instead.Puritan
why var x = vs let x = or const perhaps.Puritan
P
26

Use event delegation for the dynamically created elements. With this, you only need one event listener on the ul#list and it will work for all elements you dynamically attach to it:

document.getElementById("list").addEventListener("click",function(e) {
  if (e.target && e.target.matches("li.item")) {
    e.target.className = "foo"; // new class name here
    }
});

Here's a simplified example so you can see what happens with the code:

function addItem(i) {
  var li = document.createElement('li');
  li.appendChild(document.createTextNode(i));
  li.className = 'item';
  document.getElementById('list').appendChild(li);
}

var counter = 2;
document.getElementById('btn').addEventListener('click', function() {
  addItem(counter++);
});

document.getElementById("list").addEventListener("click", function(e) {
  if (e.target && e.target.matches("li.item")) {
    e.target.className = "foo"; // new class name here
    alert("clicked " + e.target.innerText);
  }
});
<ul id="list">
  <li class="item">1</li>
</ul>

<button id="btn">
  add item
</button>
Pierides answered 8/2, 2017 at 11:33 Comment(1)
That seems like the best option and works great, but can I also add another script that allows user to click that "li": item again to change class back to "item"? I've tried adding document.getElementById("list").addEventListener("click",function(e) { if (e.target && e.target.matches("li.itemDone")) { e.target.className = "item"; } }); but that doesn't work. When I put it into "else" it also doesn't work.Cristacristabel
A
3

You'll have to set the eventListener on each single item, as document.getElementsByClassName() returns a collection of items and you can't simply add an event listener to all of them with one call of addEventListener().

So, just like the loop you used in itemDone(), you'll have to iterate over all items and add the listener to them:

var items = document.getElementsByClassName("item");
for (var i = 0; i < items.length; i++) {
  items[i].addEventListener("click", itemDone);
}

As pointed out in the comments, you can also do so directly when creating the elements, so in your addItem() function, add:

newListItem.addEventListener("click", itemDone);
Ariana answered 8/2, 2017 at 11:33 Comment(0)
E
2

try this :

var item = document.getElementsByClassName("item");
for (var i = 0; i < item.length; i++) {
  item[i].addEventListener("click", itemDone);
}
Encincture answered 8/2, 2017 at 11:30 Comment(0)
C
2

function addItem(i) {
  var li = document.createElement('li');
  li.appendChild(document.createTextNode(i));
  li.className = 'item';
  document.getElementById('list').appendChild(li);
}

var counter = 2;
document.getElementById('btn').addEventListener('click', function() {
  addItem(counter++);
});

document.getElementById("list").addEventListener("click", function(e) {
  if (e.target && e.target.matches("li.item")) {
    e.target.className = "foo"; // new class name here
    alert("clicked " + e.target.innerText);
  }
});
<ul id="list">
  <li class="item">1</li>
  <li class="item">1</li>
  <li class="item">1</li>
  <li class="item">1</li>
  <li class="item">1</li>
</ul>

<button id="btn">
  add item
</button>
Cutshall answered 23/11, 2017 at 5:44 Comment(0)
C
0

First, try using getElementByTagName instead of querySelectorAll, because querySelectorAll is slower. And second, item receives an array, so item.addEventListener will give you an error. You have to do the addEventListener over item[counter], in a loop.

Chronological answered 8/2, 2017 at 11:33 Comment(1)
Perhaps put a snippet with the updates applied to the OP's code to demonstrate and clarify your change suggestionPuritan
E
0
document.getElementById("list").addEventListener("click", function (e) {
        const parent = e.target.parentElement;
        const siblings = Array.from(parent.getElementsByTagName("LI"));
        siblings.forEach(n => (n.className = (e.target === n) ? "foo" : ""));
      });
Erv answered 14/12, 2021 at 3:56 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Volotta
S
0

Though @baoo answer is correct if we do not have nested html elements inside the li. However, If we have HTML elements inside the li as well then it will fail.

To deal with such scenario you can use below code snippet. This way you do not need to put extra checks as well.

const list = document.getElementById("list");
list.addEventListener("click",function(event) {
  event.target.closest("li.item").classList.add("foo");
});
Stern answered 1/10, 2024 at 15:10 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.