I want to build a todo list.
I managed to get all eventListeners to work for the first task, but for each subsequent task these listeners are removed or not assigned at all. I tried it with a forEach loop, but either I used it wrong or it didn’t work. Now I don’t understand exactly what I can change so that all further tasks also listen to these eventListeners.
I tried using a forEach loop on the close eventListener but then the whole closing eventListener stopped working on every task.
const FORM = document.getElementById("form");
let input = document.getElementById("text");
const CLOSE = document.getElementById("close");
const WRAPPER = document.querySelector(".newWrapper");
const TASK = document.getElementById("task");
FORM.addEventListener("submit", (evt) => {
evt.preventDefault();
let div = document.createElement("div");
div.classList.add("newWrapper");
let p = document.createElement("p");
let renderedText = document.createTextNode(input.value);
p.setAttribute("id", "task");
p.setAttribute("class", "w-96 bg-white text-center py-3");
let i = document.createElement("i");
i.setAttribute("id", "close")
i.setAttribute("class", "fa fa-x fa-2x h-12 p-2 cursor-pointer");
p.appendChild(renderedText);
div.appendChild(p);
div.appendChild(i)
document.body.appendChild(div);
});
CLOSE.addEventListener("click", () => {
document.body.removeChild(WRAPPER)
});
TASK.addEventListener("click", () => {
WRAPPER.setAttribute("class", "flex justify-center");
if (TASK.className !== "checked w-96 py-3 text-center") {
TASK.setAttribute("class", "checked w-96 py-3 text-center");
} else {
TASK.setAttribute("class", "w-96 bg-white text-center py-3");
}
});
<script src="https://cdn.tailwindcss.com"></script>
<div class="flex justify-center ">
<h1 class="text-3xl">Todos</h1>
</div>
<form id="form" class="flex justify-center items-center">
<input id="text" type="text" class="w-80 bg-white rounded-md py-3" placeholder="Essen gehen...">
<i type="submit" class="fa-solid fa-check hover:cursor-pointer"></i>
</form>
<br>
<br>
<div class="newWrapper">
<p id="task" class="w-96 bg-white text-center py-3">get food</p>
<i id="close" class="fa fa-x fa-2x h-12 p-2 cursor-pointer"></i>
</div>
<div class="newWrapper">
<p id="task" class="w-96 bg-white text-center py-3">get food</p>
<i id="close" class="fa fa-x fa-2x h-12 p-2 cursor-pointer"></i>
</div>
3
Answers
ID values are required to be unique in any given document.
getElementById
only returns a single element. (In an invalid document reusing the sameid
value, it’ll return the first one, though I don’t know that that’s specified anywhere. Don’t do it.)Instead, use a class. Then, either:
Hook the event on an element that all of these elements are inside (the
body
element if there’s nothing more specific), and then take action if the event passed through one of these elements. This is called "event delegation."One of the keys to that is the
closest
method on an element, which finds the first element in the element’s ancestry (starting with the element itself) that matches a given CSS selector.The commented-out call to
contains
there isn’t needed if you’re doing this withdocument.body
, but you may need it if using delegation deeper in the DOM tree, to avoid matching elements that are ancestors to the one where you’re handling the event. It’s relatively rare, but worth noting.Look up all the matching elements (for instance, via
document.querySelectorAll
) and hook up the handler to each of them.There are pros and cons to each. Event delegation is particularly well-suited to situations where you’re adding and removing elements dynamically, though it works just fine if you aren’t, as well. It’s slightly more sensitive to interference from other event handlers for the same event.
More to explore:
id
attributegetElementById
document.querySelectorAll
closest
contains
Please note that
id
can’t be same to many html elements, instead useclass
.Here it is an example, have a look, understand it, then apply the thing to your code.
Here is your code using delegation.
I added relevant libraries too
I add the todos to its own div to delegate from there