You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

问题根源

  1. 循环闭包陷阱:for循环中绑定的事件引用了循环变量i,闭包会保留对i的引用而非当前迭代值。循环结束后i等于数组长度,触发事件时会使用这个最终值,导致操作错误的数组索引。
  2. 无唯一任务标识:LocalStorage存储的是纯字符串数组,仅依赖数组索引定位任务,当数组元素顺序变化(如删除中间项),索引与任务的对应关系会错乱。
  3. 新增任务逻辑缺失:原代码未处理表单提交事件,新增任务没有绑定删除/修改事件,刷新后这些任务的按钮自然失效。

修复后的完整代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 02:17:14