Search code examples
javascripthtmlevent-listenerdom-manipulation

JavaScript Todo List: Trash Icon Click Event Not Deleting List Item


I'm working on a simple JavaScript project where I'm building a todo list. The goal is to allow users to add new todo items and delete them by clicking a trash icon. However, I'm having trouble getting the delete functionality to work as expected.

What I Tried:

Adding Event Listener: I attached an event listener directly to the trash icon (<i>) inside the addNewTodo() function. This listener is supposed to trigger when the icon is clicked and then remove the corresponding <li> element from the DOM.

Logging the Click Event: I added a console.log statement inside the event listener to verify that the click event is firing. This log message isn't working because of the click event isn't firing.

Removing the Todo Item: Within the same event listener, I called the remove() method on the parent <li> element (newTodoLi()) to remove the todo item from the list. My expectation was that clicking the trash icon would delete the corresponding todo item.

What I Expected:

I expected that each time I clicked on a trash icon next to a todo item, the item would be immediately removed from the list without any issues. Since the event listener seemed to be firing correctly, I anticipated that calling remove() on the parent <li> would reliably delete the item.

let inputElem   = document.querySelector("input");
let addTodoForm = document.querySelector(".add");
let todoUlElem  = document.querySelector(".todos");

function addNewTodo(newTodoValue) {
  // Create the new li element
  let newTodoLi = document.createElement("li");
  newTodoLi.className =
    "list-group-item d-flex justify-content-between align-items-center";

  // Create the span for the todo title
  let newTodoTitleSpan = document.createElement("span");
  newTodoTitleSpan.innerHTML = newTodoValue;

  // Create the trash icon element
  let newTodoTrash = document.createElement("i");
  newTodoTrash.className = "fa fa-trash delete";

  // Append the title span and trash icon to the new li element
  newTodoLi.append(newTodoTitleSpan, newTodoTrash);

  // Append the new li element to the ul element
  todoUlElem.append(newTodoLi);

  // Add an event listener to the trash icon
  newTodoTrash.addEventListener("click", (e) => {
    console.log("clicked delete icon");

    // You can add more functionality here, like removing the todo item
    // newTodoLi.remove();
  });
}

// Prevent the default form submission
addTodoForm.addEventListener("submit", (e) => {
  e.preventDefault();
});

// Add a new todo item when Enter key is pressed
inputElem.addEventListener("keydown", (e) => {
  let newTodoValue = e.target.value.trim();
  if (e.keyCode === 13) {
    inputElem.value = "";
    if (newTodoValue) {
      addNewTodo(newTodoValue);
    }
  }

});
body {
  background: #353f5b;
}

.container {
  max-width: 400px;
  margin-top: 90px;
}

.search i {
  position: absolute;
}

.search {
  position: relative;
}

.icon {
  padding: 10px;
  left: 0;
}

.input-field {
  text-align: center;
}

input[type="text"],
input[type="text"]:focus {
  color: #fff;
  border: none;
  background: rgba(0, 0, 0, 0.3);
  max-width: 400px;
}

.todos li {
  background: #423a6f;
}

.delete {
  cursor: pointer;
}

.filtered {
  display: none !important;
}

span {
  color: #fff;
}
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" integrity="sha512-q3eWabyZPc1XTCmF+8/LuE1ozpg5xxn7iO89yfSOd5/oKvyqLngoNGsx8jq92Y8eXJ/IRxQbEC+FGSYxtk2oiw==" crossorigin="anonymous" referrerpolicy="no-referrer" />

<!-- originally=> <script src="FontAwesome5/all.min.js"></script> -->     
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/js/all.min.js" integrity="sha512-LW9+kKj/cBGHqnI4ok24dUWNR/e8sUD8RLzak1mNw5Ja2JYCmTXJTF5VpgFSw+VoBfpMvPScCo2DnKTIUjrzYw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

<div class="container">
  <ul class="list-group todos mx-auto text-light"></ul>

  <form class="add text-center my-4" autocomplete="off">
    <label class="text-light"> Add New Todo... </label>
    <input type="text" class="form-control m-auto" name="add" />
  </form>
</div>

I have bring all the codes to make easier the problem for you


Solution

  • First of All -> remove
    <script src="FontAwesome5/all.min.js"></script>
    because it replace your:
    <i class="fa fa-trash delete"></i>
    by a <svg> element.

    Also there some JS facility you should have to know:
    HTML templates : https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template

    Event delegation = ( Event_bubbling + .matches() method )

    --> What is event delegation, and how does it work?

    on you code:

    const
      inputElem   = document.querySelector('input')
    , addTodoForm = document.querySelector('.add')
    , todoList    = document.querySelector('.todos')
    , todoElmT    = document.querySelector("#todo-list-elm-template")
      ;
    addTodoForm.addEventListener("submit", (e) => {  e.preventDefault(); })
      ;
    inputElem.addEventListener("keydown", (e) => 
      {                       // Add a new todo item when Enter key is pressed
      if (e.key === 'Enter' )
        {
        let newTodoValue = inputElem.value.trim();
        inputElem.value  = ''; 
    
        if (newTodoValue.length > 0 ) 
          addNewTodo( newTodoValue );
        }
      });
    todoList.addEventListener("click", (e) =>     // use event delegation for this
      {  
      if (!e.target.matches('.delete') )  return; // ignore click event
      
      todoList.removeChild(e.target.closest('li'));
      });
    function addNewTodo( newTodoValue ) // HTML template is usefull
      {
      const todoElm                 = todoElmT.content.cloneNode(true);
      todoElm.querySelector('span')
        .textContent                = newTodoValue;
    
      todoList.append(todoElm);
      }
    body {
      background: #353f5b !important;
      }
    .container {
      max-width  : 400px;
      margin-top : 90px;
      }
    .search i {
      position   : absolute;
      }
    .search {
      position   : relative;
      }
    .icon {
      padding : 10px;
      left    : 0;
      }
    .input-field {
      text-align: center;
      }
    input[type="text"],
    input[type="text"]:focus {
      color      : #fff;
      border     : none;
      background : rgba(0, 0, 0, 0.3);
      max-width  : 400px;
      }
    .todos li {
      background : #423a6f;
      }
    .delete {
      cursor : pointer;
      color  : orange
      }
    .filtered {
      display : none !important;
      }
    span {
      color   : #fff;
      }
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" integrity="sha512-q3eWabyZPc1XTCmF+8/LuE1ozpg5xxn7iO89yfSOd5/oKvyqLngoNGsx8jq92Y8eXJ/IRxQbEC+FGSYxtk2oiw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
    
    <div class="container">
      <ul class="list-group todos mx-auto text-light"></ul>
    
      <form class="add text-center my-4" autocomplete="off">
        <label class="text-light"> Add New Todo... </label>
        <input type="text" class="form-control m-auto" name="add" />
      </form>
    </div>
    
    <template id="todo-list-elm-template">
      <li class="list-group-item d-flex justify-content-between align-items-center">
        <span></span>
        <i class="fa fa-trash delete"></i>
      </li>
    </template>