我正在使用香草JavaScript开发“待办事项列表”应用。目前我有两个问题。
当我从列表底部删除项目时,删除功能可以正常工作。但是,如果我从顶部删除,它将首先从底部删除该项目,然后是第二个,最后是顶部。如何解决此问题,以便删除我刚点击的那个?
如果单击“检查”按钮,则该任务应标记为已完成,并且“ completed”类将添加到html中。但是,如果我单击“检查”按钮,则在数据结构中,似乎已成功切换“ isCompleted”的值,但将立即从html中删除类,例如0.01秒后。
有什么问题,我该如何解决?
const todoBlock = document.querySelector(".task__nav");
const submitForm = document.querySelector(".form");
const taskItem = document.querySelector(".task");
const taskContainer = document.querySelector(".todo__container");
const showAllTasksNav = document.querySelector(".task__nav-item.all");
const showCompletedTasksNav = document.querySelector(
".task__nav-item.finished"
);
const taskContainer_new = document.querySelector(".items");
let todoList = [];
todoList = JSON.parse(localStorage.getItem("todolist")) || [];
updateUI();
submitForm.addEventListener("submit", event => {
event.preventDefault();
if (taskItem.value !== "") {
addTodo(taskItem.value);
}
});
function addTodo(task) {
const todo = {
task: taskItem.value,
isCompleted: false,
id: Date.now()
};
todoList.push(todo);
updateUI();
clearInput();
}
function updateUI() {
taskContainer_new.textContent = "";
todoList.forEach(el => {
showItems(el.id, el.task);
});
localStorage.setItem("todolist", JSON.stringify(todoList));
}
function showItems(id, task) {
const markup = `
<div class="todo__item" data-key=${id}>
<p>${task}</p>
<div class="icons">
<button class="item__complete--btn"><svg class="ion-ios-checkmark-outline" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1em" height="1em" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 512 512"><path d="M340.1 177.3L215.3 303l-47.2-47.2-17.8 17.8 56 56c2.5 2.5 5.9 4.5 8.9 4.5s6.3-2 8.8-4.4l133.7-134.4-17.6-18z" fill="#626262"/><path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm0 398.7c-105.1 0-190.7-85.5-190.7-190.7 0-105.1 85.5-190.7 190.7-190.7 105.1 0 190.7 85.5 190.7 190.7 0 105.1-85.6 190.7-190.7 190.7z" fill="#626262"/></svg></button>
<button class="item__delete--btn"><svg class="ion-ios-close-outline" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1em" height="1em" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 512 512"><path d="M403.1 108.9c-81.2-81.2-212.9-81.2-294.2 0s-81.2 212.9 0 294.2c81.2 81.2 212.9 81.2 294.2 0s81.2-213 0-294.2zm-12.3 281.9c-74.3 74.3-195.3 74.3-269.6 0-74.3-74.3-74.3-195.3 0-269.6s195.3-74.3 269.6 0c74.4 74.3 74.4 195.3 0 269.6z" fill="#626262"/><path d="M340.2 160l-84.4 84.2-84-83.8-11.8 11.8 84 83.8-84 83.8 11.8 11.8 84-83.8 84.4 84.2 11.8-11.8-84.4-84.2 84.4-84.2z" fill="#626262"/></svg></button>
</div>
</div>
`;
taskContainer_new.insertAdjacentHTML("afterbegin", markup);
}
taskContainer.addEventListener("click", event => {
const target = event.target;
const id = target.parentNode.parentNode.parentNode.dataset.key;
if (target.classList.contains("ion-ios-checkmark-outline")) {
toggleTodo(id);
} else if (target.classList.contains("ion-ios-close-outline")) {
deleteTodo(target);
}
});
function clearInput() {
taskItem.value = "";
}
function toggleTodo(key) {
const index = todoList.findIndex(item => item.id === Number(key));
todoList[index].isCompleted = !todoList[index].isCompleted;
const item = document.querySelector(`[data-key="${key}"]`);
if (todoList[index].isCompleted) {
item.classList.add("completed");
console.log("class added");
} else {
item.classList.remove("completed");
}
updateUI();
}
function deleteTodo(target) {
const targetTaskId = target.parentNode.parentNode.parentNode.dataset.key;
const index = todoList.findIndex(item => item.id === Number(targetTaskId));
console.log("todoList[index]", todoList[index]);
todoList.splice(todoList[index], 1);
updateUI();
}
showAllTasksNav.addEventListener("click", displayAllTasks);
showCompletedTasksNav.addEventListener("click", displayCompletedTask);
function displayCompletedTask() {
showAllTasksNav.classList.remove("active");
showCompletedTasksNav.classList.add("active");
todoList.filter(function(index, value) {
return index.isCompleted;
});
}
function displayAllTasks() {
showCompletedTasksNav.classList.remove("active");
showAllTasksNav.classList.add("active");
updateUI();
}
<div class="wrapper">
<h1>To Do List</h1>
<div class="input__area">
<form class="form">
<input class="task" type="text" placeholder="Enter Task" />
<input class="add" type="submit" value="ADD" />
</form>
</div>
<div class="todo__container">
<ul class="task__nav">
<li class="task__nav-item all">All Tasks</li>
<li class="task__nav-item finished">Completed Tasks</li>
</ul>
<div class="items"></div>
</div>
</div>
* {
margin: 0;
padding: 0;
}
body {
font-family: 'Roboto', sans-serif;
background: linear-gradient(to right, #aaffa9, #11ffbd); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
color: #666;
}
.wrapper {
width: 80vw;
height: 80vh;
margin: 0 auto;
padding: 20px 0 0 0;
}
h1 {
color: #fff;
text-shadow: 2px 2px rgba(0,0,0,0.05);
text-align: center;
}
button {
background-color: transparent;
border: none;
transition: transform .1s;
cursor: none;
outline: none;
}
button i {
color: #666;
font-size: 150%;
padding-left: 10px;
cursor: pointer;
outline: none;
}
button:hover {
transform: scale(1.2);
cursor: pointer;
}
.task__nav {
padding: 0;
margin: 0 0 40px 0;
list-style: none;
}
.task__nav-item {
cursor: pointer;
float: right;
font-size: 80%;
margin-left: 20px;
}
.task__nav-item.active {
/* color: #eb4034; */
font-weight: bold;
}
.input__area {
margin: 10px 0 20px 0;
box-shadow: 2px 0px 2px 0px rgba(0,0,0,0.05);
width: 100%;
}
.input__area form {
display: flex;
justify-content: space-between;
width: 100%;
}
.todo__container {
background: #fff;
box-shadow: 2px 0px 2px 0px rgba(0,0,0,0.05);
padding: 20px 20px 30px 20px;
}
input {
outline: none;
}
input[type="text"] {
border: none;
width: 100%;
padding: 10px 10px;
box-sizing: border-box;
}
input[type="text"]::placeholder {
color: #ccc;
}
input[type="submit"] {
width: 20%;
padding: 10px;
border: none;
background-color: #ec5757;
color: #fff;
font-weight: bold;
}
.todo__item {
padding: 10px 10px;
border-bottom: 1px solid #ccc;
display: flex;
justify-content: space-between;
align-items: center;
}
.todo__item.completed p::after {
margin: 0 0 0 10px;
content: "Finished!";
color: #ec5757;
font-weight: bold;
font-size: 70%;
}
答案 0 :(得分:1)
Splice希望索引为第一个参数,第二个参数代表要删除的项目数
array.splice(index, howmany, item1, ....., itemX)
尝试更改todoList.splice(todoList [index],1);到
todoList.splice(index, 1);
对于第二个问题,您不必在类列表中添加或删除“完成的”类,则可以在showItems方法中创建html标记时有条件地进行处理
function showItems(id, task) {
const completedClassName = todoList.find(e => element.id === id).isCompleted ? 'completed' : '';
const markup = `
<div class="todo__item ${completedClassName}" data-key=${id}>
... remaining markup
</div>
`;
taskContainer_new.insertAdjacentHTML("afterbegin", markup);
}
答案 1 :(得分:1)
在deleteTodo
函数中,您可以使用Array.filter
方法从todoList
数组中删除一项:
function deleteTodo(target) {
const targetTaskId = target.parentNode.parentNode.parentNode.dataset.key;
todoList = todoList.filter(function(item) {
return item.id !== Number(targetTaskId);
});
updateUI();
}
关于您的第二个问题,completed
类立即被“删除”的原因是,您的showItems
函数重新提供了待办事项,而没有考虑到某些项目应具有{在completed
属性内添加了{1}}:
class
这是工作代码:
答案 2 :(得分:1)
第一个问题是因为您没有将待删除的待办事项索引传递给splice
函数
如下所示更改deleteTodo
功能
function deleteTodo(target) {
const targetTaskId = target.closest('.todo__item').dataset.key;
const index = todoList.findIndex(item => item.id === Number(targetTaskId));
todoList.splice(index, 1);
updateUI();
}
第二个问题是因为您要在todo的容器元素上添加completed
类,但之后立即调用updateUI
函数,该函数将删除completed
课。
如下所示更改toggleTodo
功能
function toggleTodo(key) {
const index = todoList.findIndex(item => item.id === Number(key));
todoList[index].isCompleted = !todoList[index].isCompleted;
updateUI();
}
在调用updateUI
函数时,您需要传递当前待办事项是否标记为已完成。如果是,则showItems
函数应添加completed
类
您的updateUI
函数应更改为
function updateUI() {
taskContainer_new.textContent = "";
todoList.forEach(el => {
showItems(el.id, el.task, el.isCompleted);
});
localStorage.setItem("todolist", JSON.stringify(todoList));
}
和showItems
用作
function showItems(id, task, isCompleted) {
const markup = `
<div class="todo__item ${isCompleted ? 'completed' : ''}" data-key=${id}>
<p>${task}</p>
<div class="icons">
<button class="item__complete--btn"><svg class="ion-ios-checkmark-outline" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1em" height="1em" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 512 512"><path d="M340.1 177.3L215.3 303l-47.2-47.2-17.8 17.8 56 56c2.5 2.5 5.9 4.5 8.9 4.5s6.3-2 8.8-4.4l133.7-134.4-17.6-18z" fill="#626262"/><path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm0 398.7c-105.1 0-190.7-85.5-190.7-190.7 0-105.1 85.5-190.7 190.7-190.7 105.1 0 190.7 85.5 190.7 190.7 0 105.1-85.6 190.7-190.7 190.7z" fill="#626262"/></svg></button>
<button class="item__delete--btn"><svg class="ion-ios-close-outline" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1em" height="1em" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 512 512"><path d="M403.1 108.9c-81.2-81.2-212.9-81.2-294.2 0s-81.2 212.9 0 294.2c81.2 81.2 212.9 81.2 294.2 0s81.2-213 0-294.2zm-12.3 281.9c-74.3 74.3-195.3 74.3-269.6 0-74.3-74.3-74.3-195.3 0-269.6s195.3-74.3 269.6 0c74.4 74.3 74.4 195.3 0 269.6z" fill="#626262"/><path d="M340.2 160l-84.4 84.2-84-83.8-11.8 11.8 84 83.8-84 83.8 11.8 11.8 84-83.8 84.4 84.2 11.8-11.8-84.4-84.2 84.4-84.2z" fill="#626262"/></svg></button>
</div>
</div>
`;
taskContainer_new.insertAdjacentHTML("afterbegin", markup);
}
这里是working demo,与您的代码不同,它还显示了已完成任务和所有正确任务的列表