Search code examples
javascriptcodesandboxtypescript

How to set up remove item buttons in vanilla-typescript todo app


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


Solution

  • 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");
    });