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>
Advertisement
Answer
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>