Search code examples
javascriptget-childitem

Uncaught TypeError: Cannot read property of a todolist


Uncaught TypeError: Cannot read property 'contains' of undefined

Uncaught TypeError: Cannot set property 'display' of undefined

I am trying to loop through todoList and get the child element. Where I am wrong? The child element is the list item. The list items are dynamically added on adding a todo.

const todoInput = document.querySelector('.todo-input');
const todoList = document.querySelector('.todo-list');
const filterOption = document.querySelector(".filter-todo");
const todoButton = document.querySelector(".todo-button");

todoButton.addEventListener("click", addTodo);
filterOption.addEventListener("click", filterTodo);

function addTodo(e) {
  e.preventDefault();
  console.log("hello");
  //todo div
  const todoDiv = document.createElement("div");
  todoDiv.classList.add("todo");
  //create li
  const newTodo = document.createElement('li');
  newTodo.innerText = todoInput.value;
  newTodo.classList.add('todo-item')
  todoDiv.appendChild(newTodo);
  //check mark button
  const completedButton = document.createElement('button');
  completedButton.innerHTML = '<i class="fas fa-check"></i>';
  completedButton.classList.add("complete-btn");
  todoDiv.appendChild(completedButton);
  //check trash button
  const trashButton = document.createElement('button');
  trashButton.innerHTML = '<i class="fas fa-trash"></i>';
  trashButton.classList.add("trash-btn");
  todoDiv.appendChild(trashButton);

  //append to list
  todoList.appendChild(todoDiv)
  //clear todo input value
  todoInput.value = "";

}

function filterTodo(e) {
  const todos = todoList.childNodes;
  todos.forEach(function(todo) {
    console.log(todo);
    switch (e.target.value) {
      case "all":
        todo.style.display = "flex";
        break;
      case "completed":
        if (todo.classList.contains("completed")) {
          todo.style.display = "flex";
        } else {
          todo.style.display = "none";
        }
        break;
      case "uncompleted":
        if (!todo.classList.contains("completed")) {
          todo.style.display = "flex";
        } else {
          todo.style.display = "none";
        }
    }
  });
}
.todo-container {
  display: flex;
  justify-content: center;
  align-items: center;
}

.todo-list {
  min-width: 30%;
  list-style: none;
}

.todo {
  margin: 0.5rem;
  background: white;
  color: black;
  font-size: 1.5rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
  transition: all 0.5s ease;
}

.todo li {
  flex: 1;
}

.trash-btn,
.complete-btn {
  background: #ff6f47;
  color: white;
  border: none;
  padding: 1rem;
  cursor: pointer;
  font-size: 1rem;
}

.complete-btn {
  background: rgb(73, 204, 73);
}

.todo-item {
  padding: 0rem 0.5rem;
}

.fa-trash,
.fa-check {
  pointer-events: none;
}

.completed {
  text-decoration: line-through;
  opacity: 0.5;
}

.fall {
  transform: translateY(8rem) rotateZ(20deg);
  opacity: 0;
}

select {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  outline: none;
  border: none;
}

.select {
  margin: 1rem;
  position: relative;
  overflow: hidden;
}

select {
  color: #ff6f47;
  width: 10rem;
  cursor: pointer;
  padding: 1rem;
}

.select::after {
  content: "\25BC";
  position: absolute;
  background: #ff6f47;
  top: 0;
  right: 0;
  padding: 1rem;
  pointer-events: none;
  transition: all 0.3s ease;
}

.select:hover::after {
  background: white;
  color: #ff6f47;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" />
<form>
  <input type="text" class="todo-input">
  <button class="todo-button" type="submit">
            <i class="fas fa-plus-square"></i>
        </button>
  <div class="select">
    <select name="todos" class="filter-todo">
      <option value="all">All</option>
      <option value="completed">Completed</option>
      <option value="uncompleted">Uncompleted</option>
    </select>
  </div>
</form>
<div class="todo-container">
  <ul class="todo-list">
    <!---Adding todos dynamically-->
  </ul>
</div>


Solution

  • Quite a bit of stuff to rewrite

    I added the completed and delete functionality

    const todoInput = document.querySelector('.todo-input');
    const todoList = document.querySelector('.todo-list');
    const filterOption = document.querySelector(".filter-todo");
    const todoButton = document.querySelector(".todo-button");
    
    todoButton.addEventListener("click", addTodo);
    filterOption.addEventListener("change", filterTodo);
    todoList.addEventListener("click", handleTodo)
    
    function handleTodo(e) {
      const tgt = e.target.closest("button");
      if (tgt) {
        if (tgt.classList.contains("complete-btn")) {
          tgt.closest("div").classList.add("completed");
        } else if (tgt.classList.contains("trash-btn")) {
          tgt.closest("div").remove();
        }
      }
    }
    
    function addTodo(e) {
      e.preventDefault();
      console.log("hello");
      //todo div
      const todoDiv = document.createElement("div");
      todoDiv.classList.add("todo");
      //create li
      const newTodo = document.createElement('li');
      newTodo.innerText = todoInput.value;
      newTodo.classList.add('todo-item')
      todoDiv.appendChild(newTodo);
      //check mark button
      const completedButton = document.createElement('button');
      completedButton.innerHTML = '<i class="fas fa-check"></i>';
      completedButton.classList.add("complete-btn");
      todoDiv.appendChild(completedButton);
      //check trash button
      const trashButton = document.createElement('button');
      trashButton.innerHTML = '<i class="fas fa-trash"></i>';
      trashButton.classList.add("trash-btn");
      todoDiv.appendChild(trashButton);
    
      //append to list
      todoList.appendChild(todoDiv)
      //clear todo input value
      todoInput.value = "";
    
    }
    
    function filterTodo(e) {
      const todos = todoList.querySelectorAll(".todo-list div");
      const val = e.target.value;
      todos.forEach(function(todo) {
        const show = val === "all" ||
          (val === "completed" && todo.classList.contains("completed")) ||
          (val === "uncompleted" && !todo.classList.contains("completed"));
        todo.classList.toggle("hide", !show)
    
      });
    }
    .todo-container {
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    .todo-list {
      min-width: 30%;
      list-style: none;
    }
    
    .todo {
      margin: 0.5rem;
      background: white;
      color: black;
      font-size: 1.5rem;
      display: flex;
      justify-content: space-between;
      align-items: center;
      transition: all 0.5s ease;
    }
    
    .todo li {
      flex: 1;
    }
    
    .trash-btn,
    .complete-btn {
      background: #ff6f47;
      color: white;
      border: none;
      padding: 1rem;
      cursor: pointer;
      font-size: 1rem;
    }
    
    .complete-btn {
      background: rgb(73, 204, 73);
    }
    
    .todo-item {
      padding: 0rem 0.5rem;
    }
    
    .fa-trash,
    .fa-check {
      pointer-events: none;
    }
    
    .completed {
      text-decoration: line-through;
      opacity: 0.5;
    }
    
    .fall {
      transform: translateY(8rem) rotateZ(20deg);
      opacity: 0;
    }
    
    select {
      -webkit-appearance: none;
      -moz-appearance: none;
      appearance: none;
      outline: none;
      border: none;
    }
    
    .select {
      margin: 1rem;
      position: relative;
      overflow: hidden;
    }
    
    select {
      color: #ff6f47;
      width: 10rem;
      cursor: pointer;
      padding: 1rem;
    }
    
    .select::after {
      content: "\25BC";
      position: absolute;
      background: #ff6f47;
      top: 0;
      right: 0;
      padding: 1rem;
      pointer-events: none;
      transition: all 0.3s ease;
    }
    
    .select:hover::after {
      background: white;
      color: #ff6f47;
    }
    
    .hide {
      display: none
    }
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" />
    
    <form>
      <input type="text" class="todo-input">
      <button class="todo-button" type="submit">
                <i class="fas fa-plus-square"></i>
            </button>
      <div class="select">
        <select name="todos" class="filter-todo">
          <option value="all">All</option>
          <option value="completed">Completed</option>
          <option value="uncompleted">Uncompleted</option>
        </select>
      </div>
    </form>
    <div class="todo-container">
      <ul class="todo-list">
        <!---Adding todos dynamically-->
      </ul>
    </div>