在原版 javascript 待办事项列表中过滤待办事项
Filter todo items in vanilla javascript todo list
我正在构建一个待办事项列表,现在我一直在尝试实现 HTML-select 下拉列表以在已完成和未完成的待办事项之间进行过滤。
我正在使用 switch 语句 select 要执行的各个块,但是我不断收到以下错误“Uncaught TypeError: Cannot set 属性 'display' of undefined”,但我不明白什么元素未定义。
const input = document.getElementById('input')
const addTodoButton = document.getElementById('addTodoButton')
const todoUL = document.getElementById('todoUL')
const filterOptions = document.querySelector('.filter-todos')
addTodoButton.addEventListener('click', addTodo)
todoUL.addEventListener('click', remove);
filterOptions.addEventListener('click', filterTodos);
// Add todo
function addTodo(e) {
e.preventDefault()
const todoText = input.value
const todoEl = `<li><span>${todoText}</span> <button class="delete" id="deleteTodoButton"><i class="far fa-trash-alt"></i>Delete</button> <button class="complete" id="completeTodoButton"><i class="fas fa-check"></i>Completed</button></li>`
input.value = ""
input.focus()
if (!todoText) {
alert('You must type a todo')
} else {
todoUL.insertAdjacentHTML("beforeend", todoEl)
}
}
// Remove/Complete todo
function remove(e) {
if (e.target.id == 'deleteTodoButton') {
e.target.parentElement.remove()
input.focus()
} else {
e.target.previousElementSibling.previousElementSibling.classList.toggle('completed')
input.focus()
}
}
function filterTodos(e) {
const todos = todoUL.childNodes
todos.forEach(function (todoEl) {
switch (e.target.value) {
case "all":
todoEl.style.display = "flex"
break;
case "completed":
if (todoEl.classList.contains("completed")) {
todoEl.style.display = "flex"
} else {
todoEl.style.display = "none"
}
break;
}
})
}
ul {
list-style: none
}
.completed {
text-decoration: line-through
}
<div class="form-container">
<h1>Todo List App</h1>
<form id="form">
<input type="text" id="input" autocomplete="off" placeholder="Enter your todo">
<button type="submit" class="add-todo" id="addTodoButton">Add</button>
<select name="todos" class="filter-todos">
<option value="all">All</option>
<option value="completed">Completed</option>
<option value="uncompleted">Uncompleted</option>
</select>
</form>
<ul id="todoUL">
</ul>
</div>
const input = document.getElementById('input')
const addTodoButton = document.getElementById('addTodoButton')
const todoUL = document.getElementById('todoUL')
const filterOptions = document.querySelector('.filter-todos')
addTodoButton.addEventListener('click', addTodo)
todoUL.addEventListener('click', remove);
filterOptions.addEventListener('click', filterTodos);
// Add todo
function addTodo(e) {
e.preventDefault()
const todoText = input.value
const todoEl = `<li><span>${todoText}</span> <button class="delete" id="deleteTodoButton"><i class="far fa-trash-alt"></i>Delete</button> <button class="complete" id="completeTodoButton"><i class="fas fa-check"></i>Completed</button></li>`
input.value = ""
input.focus()
if (!todoText) {
alert('You must type a todo')
} else {
todoUL.insertAdjacentHTML("beforeend", todoEl)
}
}
// Remove/Complete todo
function remove(e) {
if (e.target.id == 'deleteTodoButton') {
e.target.parentElement.remove()
input.focus()
} else {
e.target.previousElementSibling.previousElementSibling.classList.toggle('completed')
input.focus()
}
}
function filterTodos(e) {
const todos = todoUL.querySelectorAll('li > span');
todos.forEach(function (todoEl) {
const tgt = todoEl.parentElement;
switch (e.target.value) {
case "all":
tgt.style.display = "flex"
break;
case "completed":
if (todoEl.classList.contains("completed")) {
tgt.style.display = "flex"
} else {
tgt.style.display = "none"
}
break;
case "uncompleted":
if (todoEl.classList.contains("completed")) {
tgt.style.display = "none"
} else {
tgt.style.display = "flex"
}
break;
}
})
}
ul {
list-style: none
}
.completed {
text-decoration: line-through
}
<div class="form-container">
<h1>Todo List App</h1>
<form id="form">
<input type="text" id="input" autocomplete="off" placeholder="Enter your todo">
<button type="submit" class="add-todo" id="addTodoButton">Add</button>
<select name="todos" class="filter-todos">
<option value="all">All</option>
<option value="completed">Completed</option>
<option value="uncompleted">Uncompleted</option>
</select>
</form>
<ul id="todoUL">
</ul>
</div>
我已经在我的解释方式中做到了这一点。希望这会在某些方面对您有所帮助。
我发现您编写的代码存在一些问题
首先,select 的 click
事件应该更改为 change
事件
其次,在 remove
函数的情况下,您必须在将其标记为已完成之前检查 e.target.id == "completeTodoButton"
,否则我们的节点 selection 将无法工作。
第三,如果是 filterTodos
函数,您必须在切换显示之前确保节点类型是 li
。我为此使用了 todoEl.nodeName === "LI"
比较。
在 switch case 中,你不能使用 todoEl.classList.contains("completed")
因为完成的 class 没有被绑定到 li
而是绑定到 span
标签里面 li
。您可以将 class 移动到 li
节点并相应地删除元素,或者如果您想继续使用当前结构,您应该使用 todoEl.children[0].classList.contains("completed")
因为 [= 的第一个子节点17=] 是 class completed
.
的跨度
你还漏掉了 switch 中的 case uncompleted
。
ul {
list-style: none;
}
.completed {
text-decoration: line-through;
}
<div class="form-container">
<h1>Todo List App</h1>
<form id="form">
<input
type="text"
id="input"
autocomplete="off"
placeholder="Enter your todo"
/>
<button type="submit" class="add-todo" id="addTodoButton">Add</button>
<select name="todos" class="filter-todos">
<option value="all">All</option>
<option value="completed">Completed</option>
<option value="uncompleted">Uncompleted</option>
</select>
</form>
<ul id="todoUL"></ul>
</div>
<script>
const input = document.getElementById("input");
const addTodoButton = document.getElementById("addTodoButton");
const todoUL = document.getElementById("todoUL");
const filterOptions = document.querySelector(".filter-todos");
addTodoButton.addEventListener("click", addTodo);
todoUL.addEventListener("click", remove);
filterOptions.addEventListener("change", filterTodos);
// Add todo
function addTodo(e) {
e.preventDefault();
const todoText = input.value;
const todoEl = `<li><span>${todoText}</span>
<button class="delete" id="deleteTodoButton"><i class="far fa-trash-alt"></i>Delete</button>
<button class="complete" id="completeTodoButton"><i class="fas fa-check"></i>Completed</button></li>`;
input.value = "";
input.focus();
if (!todoText) {
alert("You must type a todo");
} else {
todoUL.insertAdjacentHTML("beforeend", todoEl);
}
}
// Remove/Complete todo
function remove(e) {
if (e.target.id == "deleteTodoButton") {
e.target.parentElement.remove();
input.focus();
} else if (e.target.id == "completeTodoButton") {
e.target.previousElementSibling.previousElementSibling.classList.toggle(
"completed"
);
input.focus();
}
}
function filterTodos(e) {
const todos = todoUL.childNodes;
todos.forEach(function(todoEl) {
if (todoEl.nodeName === "LI") {
switch (e.target.value) {
case "all":
todoEl.style.display = "flex";
break;
case "completed":
if (todoEl.children[0].classList.contains("completed")) {
todoEl.style.display = "flex";
} else {
todoEl.style.display = "none";
}
break;
case "uncompleted":
if (todoEl.children[0].classList.contains("completed")) {
todoEl.style.display = "none";
} else {
todoEl.style.display = "flex";
}
break;
}
}
});
}
</script>
我正在构建一个待办事项列表,现在我一直在尝试实现 HTML-select 下拉列表以在已完成和未完成的待办事项之间进行过滤。 我正在使用 switch 语句 select 要执行的各个块,但是我不断收到以下错误“Uncaught TypeError: Cannot set 属性 'display' of undefined”,但我不明白什么元素未定义。
const input = document.getElementById('input')
const addTodoButton = document.getElementById('addTodoButton')
const todoUL = document.getElementById('todoUL')
const filterOptions = document.querySelector('.filter-todos')
addTodoButton.addEventListener('click', addTodo)
todoUL.addEventListener('click', remove);
filterOptions.addEventListener('click', filterTodos);
// Add todo
function addTodo(e) {
e.preventDefault()
const todoText = input.value
const todoEl = `<li><span>${todoText}</span> <button class="delete" id="deleteTodoButton"><i class="far fa-trash-alt"></i>Delete</button> <button class="complete" id="completeTodoButton"><i class="fas fa-check"></i>Completed</button></li>`
input.value = ""
input.focus()
if (!todoText) {
alert('You must type a todo')
} else {
todoUL.insertAdjacentHTML("beforeend", todoEl)
}
}
// Remove/Complete todo
function remove(e) {
if (e.target.id == 'deleteTodoButton') {
e.target.parentElement.remove()
input.focus()
} else {
e.target.previousElementSibling.previousElementSibling.classList.toggle('completed')
input.focus()
}
}
function filterTodos(e) {
const todos = todoUL.childNodes
todos.forEach(function (todoEl) {
switch (e.target.value) {
case "all":
todoEl.style.display = "flex"
break;
case "completed":
if (todoEl.classList.contains("completed")) {
todoEl.style.display = "flex"
} else {
todoEl.style.display = "none"
}
break;
}
})
}
ul {
list-style: none
}
.completed {
text-decoration: line-through
}
<div class="form-container">
<h1>Todo List App</h1>
<form id="form">
<input type="text" id="input" autocomplete="off" placeholder="Enter your todo">
<button type="submit" class="add-todo" id="addTodoButton">Add</button>
<select name="todos" class="filter-todos">
<option value="all">All</option>
<option value="completed">Completed</option>
<option value="uncompleted">Uncompleted</option>
</select>
</form>
<ul id="todoUL">
</ul>
</div>
const input = document.getElementById('input')
const addTodoButton = document.getElementById('addTodoButton')
const todoUL = document.getElementById('todoUL')
const filterOptions = document.querySelector('.filter-todos')
addTodoButton.addEventListener('click', addTodo)
todoUL.addEventListener('click', remove);
filterOptions.addEventListener('click', filterTodos);
// Add todo
function addTodo(e) {
e.preventDefault()
const todoText = input.value
const todoEl = `<li><span>${todoText}</span> <button class="delete" id="deleteTodoButton"><i class="far fa-trash-alt"></i>Delete</button> <button class="complete" id="completeTodoButton"><i class="fas fa-check"></i>Completed</button></li>`
input.value = ""
input.focus()
if (!todoText) {
alert('You must type a todo')
} else {
todoUL.insertAdjacentHTML("beforeend", todoEl)
}
}
// Remove/Complete todo
function remove(e) {
if (e.target.id == 'deleteTodoButton') {
e.target.parentElement.remove()
input.focus()
} else {
e.target.previousElementSibling.previousElementSibling.classList.toggle('completed')
input.focus()
}
}
function filterTodos(e) {
const todos = todoUL.querySelectorAll('li > span');
todos.forEach(function (todoEl) {
const tgt = todoEl.parentElement;
switch (e.target.value) {
case "all":
tgt.style.display = "flex"
break;
case "completed":
if (todoEl.classList.contains("completed")) {
tgt.style.display = "flex"
} else {
tgt.style.display = "none"
}
break;
case "uncompleted":
if (todoEl.classList.contains("completed")) {
tgt.style.display = "none"
} else {
tgt.style.display = "flex"
}
break;
}
})
}
ul {
list-style: none
}
.completed {
text-decoration: line-through
}
<div class="form-container">
<h1>Todo List App</h1>
<form id="form">
<input type="text" id="input" autocomplete="off" placeholder="Enter your todo">
<button type="submit" class="add-todo" id="addTodoButton">Add</button>
<select name="todos" class="filter-todos">
<option value="all">All</option>
<option value="completed">Completed</option>
<option value="uncompleted">Uncompleted</option>
</select>
</form>
<ul id="todoUL">
</ul>
</div>
我已经在我的解释方式中做到了这一点。希望这会在某些方面对您有所帮助。
我发现您编写的代码存在一些问题
首先,select 的 click
事件应该更改为 change
事件
其次,在 remove
函数的情况下,您必须在将其标记为已完成之前检查 e.target.id == "completeTodoButton"
,否则我们的节点 selection 将无法工作。
第三,如果是 filterTodos
函数,您必须在切换显示之前确保节点类型是 li
。我为此使用了 todoEl.nodeName === "LI"
比较。
在 switch case 中,你不能使用 todoEl.classList.contains("completed")
因为完成的 class 没有被绑定到 li
而是绑定到 span
标签里面 li
。您可以将 class 移动到 li
节点并相应地删除元素,或者如果您想继续使用当前结构,您应该使用 todoEl.children[0].classList.contains("completed")
因为 [= 的第一个子节点17=] 是 class completed
.
你还漏掉了 switch 中的 case uncompleted
。
ul {
list-style: none;
}
.completed {
text-decoration: line-through;
}
<div class="form-container">
<h1>Todo List App</h1>
<form id="form">
<input
type="text"
id="input"
autocomplete="off"
placeholder="Enter your todo"
/>
<button type="submit" class="add-todo" id="addTodoButton">Add</button>
<select name="todos" class="filter-todos">
<option value="all">All</option>
<option value="completed">Completed</option>
<option value="uncompleted">Uncompleted</option>
</select>
</form>
<ul id="todoUL"></ul>
</div>
<script>
const input = document.getElementById("input");
const addTodoButton = document.getElementById("addTodoButton");
const todoUL = document.getElementById("todoUL");
const filterOptions = document.querySelector(".filter-todos");
addTodoButton.addEventListener("click", addTodo);
todoUL.addEventListener("click", remove);
filterOptions.addEventListener("change", filterTodos);
// Add todo
function addTodo(e) {
e.preventDefault();
const todoText = input.value;
const todoEl = `<li><span>${todoText}</span>
<button class="delete" id="deleteTodoButton"><i class="far fa-trash-alt"></i>Delete</button>
<button class="complete" id="completeTodoButton"><i class="fas fa-check"></i>Completed</button></li>`;
input.value = "";
input.focus();
if (!todoText) {
alert("You must type a todo");
} else {
todoUL.insertAdjacentHTML("beforeend", todoEl);
}
}
// Remove/Complete todo
function remove(e) {
if (e.target.id == "deleteTodoButton") {
e.target.parentElement.remove();
input.focus();
} else if (e.target.id == "completeTodoButton") {
e.target.previousElementSibling.previousElementSibling.classList.toggle(
"completed"
);
input.focus();
}
}
function filterTodos(e) {
const todos = todoUL.childNodes;
todos.forEach(function(todoEl) {
if (todoEl.nodeName === "LI") {
switch (e.target.value) {
case "all":
todoEl.style.display = "flex";
break;
case "completed":
if (todoEl.children[0].classList.contains("completed")) {
todoEl.style.display = "flex";
} else {
todoEl.style.display = "none";
}
break;
case "uncompleted":
if (todoEl.children[0].classList.contains("completed")) {
todoEl.style.display = "none";
} else {
todoEl.style.display = "flex";
}
break;
}
}
});
}
</script>