LocalStorage删除特定任务项异常及新增任务删除失效问题
任务管理功能的两个问题及修复方案
问题现象
- 首次点击删除按钮可正常删除LocalStorage中对应任务,但后续点击其他任务项(如第3项)时,删除的却是LocalStorage中的另一项(如第4项)。
- 删除部分元素后刷新页面,新增的任务项删除按钮完全无法工作。
用户原代码
JavaScript
let inputNewTask = document.querySelector("#new-task-input"); let tasksContainer = document.querySelector("#tasks-container"); let tasksCompletedContainer = document.querySelector("#completed-tasks-container"); let formAddTask = document.querySelector("#form-add-task"); let tasksList = []; let completedTasksList = []; let tasksID = 1; function genererTasks(){ const listTasksJson = window.localStorage.getItem("Tasks name"); const listTasks = JSON.parse(listTasksJson); for(let i = 0; i < listTasks.length; i++){ let newTaskContainer = document.createElement("div"); newTaskContainer.classList.add("new-task-container"); let newTaskInput = document.createElement("input"); newTaskInput.classList.add("input-style", "task-content"); newTaskInput.id = "task-" + tasksID; tasksID = tasksID + 1; newTaskInput.type = "text"; newTaskInput.value = listTasks[i]; newTaskInput.textContent = listTasks[i].value; let iconDelete = document.createElement("i"); iconDelete.classList.add("fa-regular", "fa-circle", "btn-style"); tasksContainer.appendChild(newTaskContainer); newTaskContainer.appendChild(iconDelete); newTaskContainer.appendChild(newTaskInput); tasksList.push(listTasks[i]); iconDelete.addEventListener("click", function(){ tasksContainer.removeChild(newTaskContainer); tasksCompletedContainer.appendChild(newTaskContainer); tasksList.splice(i,1); window.localStorage.setItem("Tasks name", JSON.stringify(tasksList)); }); newTaskInput.addEventListener("change", function () { updateTaskInLocalStorage(i, newTaskInput.value); }); } } const listTasksJson = (localStorage.getItem("Tasks name") !== null); if (listTasksJson){ genererTasks(); }
HTML
<body> <main> <div class="container"> <div class="row"> <div class="col-lg-12"> <h4>En cours</h4> <div id="tasks-container"></div> <div class="new-task-container-submit"> <form id="form-add-task" action=""> <input class="btn-style" type="submit" id="new-task-submit" value="+" title="Ajouter"> <input class="input-style" type="text" id="new-task-input" placeholder="Ajouter une tâche"> </form> </div> <h4>Terminées</h4> <div id="completed-tasks-container"></div> </div> </div> </div> </main> <script type="text/javascript" src="scripts/main.js"></script> </body>
问题根源
- 循环闭包陷阱:for循环中绑定的事件引用了循环变量
i,闭包会保留对i的引用而非当前迭代值。循环结束后i等于数组长度,触发事件时会使用这个最终值,导致操作错误的数组索引。 - 无唯一任务标识:LocalStorage存储的是纯字符串数组,仅依赖数组索引定位任务,当数组元素顺序变化(如删除中间项),索引与任务的对应关系会错乱。
- 新增任务逻辑缺失:原代码未处理表单提交事件,新增任务没有绑定删除/修改事件,刷新后这些任务的按钮自然失效。
修复后的完整代码
JavaScript
let inputNewTask = document.querySelector("#new-task-input"); let tasksContainer = document.querySelector("#tasks-container"); let completedTasksContainer = document.querySelector("#completed-tasks-container"); let formAddTask = document.querySelector("#form-add-task"); let tasksList = []; let completedTasksList = []; // 从LocalStorage加载任务数据 function loadTasks() { const savedTasks = window.localStorage.getItem("Tasks"); if (savedTasks) tasksList = JSON.parse(savedTasks); const savedCompletedTasks = window.localStorage.getItem("CompletedTasks"); if (savedCompletedTasks) completedTasksList = JSON.parse(savedCompletedTasks); } // 保存任务数据到LocalStorage function saveTasks() { window.localStorage.setItem("Tasks", JSON.stringify(tasksList)); window.localStorage.setItem("CompletedTasks", JSON.stringify(completedTasksList)); } // 创建单个任务DOM元素并绑定事件 function createTaskElement(task, isCompleted = false) { const taskContainer = document.createElement("div"); taskContainer.classList.add("new-task-container"); taskContainer.dataset.taskId = task.id; // 将任务ID绑定到DOM元素 const taskInput = document.createElement("input"); taskInput.classList.add("input-style", "task-content"); taskInput.type = "text"; taskInput.value = task.content; if (isCompleted) taskInput.disabled = true; // 已完成任务禁用编辑 const actionIcon = document.createElement("i"); actionIcon.classList.add("fa-regular", isCompleted ? "fa-check-circle" : "fa-circle", "btn-style"); // 绑定点击事件:待办任务标记完成,已完成任务可标记回待办 actionIcon.addEventListener("click", () => { if (!isCompleted) { // 移至已完成列表 tasksList = tasksList.filter(t => t.id !== task.id); completedTasksList.push(task); taskContainer.remove(); completedTasksContainer.appendChild(createTaskElement(task, true)); } else { // 移回待办列表 completedTasksList = completedTasksList.filter(t => t.id !== task.id); tasksList.push(task); taskContainer.remove(); tasksContainer.appendChild(createTaskElement(task)); } saveTasks(); }); // 绑定修改事件(仅待办任务有效) if (!isCompleted) { taskInput.addEventListener("change", () => { const targetTask = tasksList.find(t => t.id === task.id); if (targetTask) { targetTask.content = taskInput.value; saveTasks(); } }); } taskContainer.appendChild(actionIcon); taskContainer.appendChild(taskInput); return taskContainer; } // 渲染待办任务列表 function renderTasks() { tasksContainer.innerHTML = ""; tasksList.forEach(task => tasksContainer.appendChild(createTaskElement(task))); } // 渲染已完成任务列表 function renderCompletedTasks() { completedTasksContainer.innerHTML = ""; completedTasksList.forEach(task => completedTasksContainer.appendChild(createTaskElement(task, true))); } // 处理新增任务表单提交 formAddTask.addEventListener("submit", e => { e.preventDefault(); const taskContent = inputNewTask.value.trim(); if (!taskContent) return; // 用时间戳生成唯一任务ID const newTask = { id: Date.now(), content: taskContent }; tasksList.push(newTask); saveTasks(); renderTasks(); inputNewTask.value = ""; }); // 页面初始化 loadTasks(); renderTasks(); renderCompletedTasks();
HTML(保持原代码不变)
<body> <main> <div class="container"> <div class="row"> <div class="col-lg-12"> <h4>En cours</h4> <div id="tasks-container"></div> <div class="new-task-container-submit"> <form id="form-add-task" action=""> <input class="btn-style" type="submit" id="new-task-submit" value="+" title="Ajouter"> <input class="input-style" type="text" id="new-task-input" placeholder="Ajouter une tâche"> </form> </div> <h4>Terminées</h4> <div id="completed-tasks-container"></div> </div> </div> </div> </main> <script type="text/javascript" src="scripts/main.js"></script> </body>
核心修复说明
- 解决闭包陷阱:将任务元素创建逻辑抽离为独立函数,每个任务的事件绑定基于当前任务对象,不再依赖循环变量。
- 唯一ID定位任务:为每个任务分配唯一ID(时间戳),通过ID精准查找和修改任务,彻底摆脱数组索引的依赖。
- 完善数据持久化:拆分加载和保存函数,统一管理待办与已完成任务的存储,确保数据同步。
- 新增任务事件绑定:添加表单提交处理,新增任务自动绑定所有必要事件,刷新后功能正常。
- 优化UI状态:区分待办与已完成任务的交互状态(如禁用已完成任务的编辑),提升用户体验。
内容的提问来源于stack exchange,提问作者Jordan Zanetti
相关产品推荐
相关产品推荐

