I am creating a basic todo list app using vanilla typescript. After appending buttons to each list item, I want the items to delete when the user clicks the button on a specific list item. To execute this, I tried adding a 2nd addEventListener method in index.ts to listen for remove button clicks, then remove the child of the parent element. My addEventListener method looks like this:
listItemBtn.addEventListener("click", (e: Event) => {
const element = e.target as HTMLUListElement;
element.removeChild(element);
});
However, I keep getting an error in the console that says "Uncaught TypeError: Cannot read property 'addEventListener' of null". I am not sure why I can't append .addEventListener to listItemBtn without errors. Any idea how to fix this?
Here is a link to the project in CodeSandBox: https://codesandbox.io/s/vanilla-typescript-forked-xnnf4
You are looking for the listItemBtn
before it exists. If you add this line
document.querySelectorAll("#listItemBtn").forEach(console.log);
at the end of your form.addEventListener
then you'll start to see it populating with elements.
There's a lot that needs to change in this code. Be cautious with using as
because it prevents Typescript from seeing potential runtime errors, like that an element might be null
.
Your question is how to set up the remove buttons, so I don't actually think that querySelectorAll
is the correct way. I would add the listener where you create btn
. At that point you already have the button
element as a variable.
Your listener function itself has problems. Again you have a troublesome as
in const element = e.target as HTMLUListElement;
because the target is the button
, not the li
.
element.removeChild(element)
is wrong because you need to be calling removeChild
on the parent, like parent.removeChild(child)
. You can look as element.parentElement
(which might be null
). Since we are moving this function into ListTemplate
then we have access to the parent li
variable. The element that we want to remove is the li
from the ul
. So actually our e.target
which is btn
isn't very useful.
Inside the render
method of ListTemplate
, do this:
btn.addEventListener("click", () => {
this.container.removeChild(li);
});
As mentioned in the comments, you should not set an id
which is not unique. You could use className
instead:
btn.className = "listItemBtn";
But you only need that for styling. We no longer query this element anywhere.
You can clean up your form
submit listener. There is no reason to create a variable and then assign it. Just assign it directly. This also removes the need to manually specify types because the type is known from the value that you set to the variable.
form.addEventListener("submit", (e) => {
e.preventDefault();
const value = listItem.value;
const doc = new ListItem(value);
list.render(doc, value, "start");
});