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

如何实现待办应用已完成、编辑任务的localStorage本地存储?

待办应用功能补全方案

你现有代码的核心问题有4个:

  • 任务数据没存「完成状态」字段,刷新后完成状态必然丢失
  • 编辑、标记完成操作只改了页面显示效果,没更新内存里的taskList数组,自然没法同步到localStorage
  • 删除逻辑有bug:indexOf直接查找id字符串,但数组里存的是任务对象,永远返回-1,会出现删错内容的问题
  • 渲染模板没闭合div标签,DOM结构错乱,而且刷新后不会自动给已完成任务加删除线样式

按下面步骤修改即可:

1. 补全任务数据结构,修复删除逻辑bug

首先给每个任务加默认的未完成标记,修正删除时的索引查找逻辑:

// 新建任务时增加isCompleted字段
function makeNewTask() {
  const data = {
    id: createId(),
    taskNew: el.input.value.trim(),
    taskDate: el.date.value,
    taskTime: el.time.value,
    isCompleted: false // 新任务默认标记为未完成
  };
  return data;
}

// 修复删除逻辑
function deleteItem(event) {
  const taskId = event.target.dataset.id;
  // 按id精准定位对应任务的索引
  const targetIndex = taskList.findIndex(item => item.id === taskId);
  if(targetIndex > -1) taskList.splice(targetIndex, 1);
  storeList();
  renderList();
}

2. 修改渲染逻辑,自动同步状态样式

修改renderList函数,补全缺失的标签闭合,根据任务完成状态自动加删除线,同时让完成按钮的文字根据状态切换为「完成/撤销完成」:

function renderList() {
  el.list.innerHTML = taskList.map(function (data) {
    // 已完成任务自动添加删除线样式
    const lineStyle = data.isCompleted ? 'text-decoration: line-through;' : '';
    return `<div class="task">
            <div class="task-content">
                <div class="task" data-id="${data.id}">
                <input class="new-task-created" value="${data.taskNew}" readonly style="${lineStyle}">
                <input class="due-date" type="date" value="${data.taskDate}" readonly style="${lineStyle}">
                <input class="due-time" type="time" value="${data.taskTime}" readonly style="${lineStyle}">
            </div>
            <div class="action-buttons">
                <button onclick="editItem(event)" class="edit" data-id="${data.id}">Edit</button>
                <button onclick="deleteItem(event)" class="delete" data-id="${data.id}">Delete</button>
                <button onclick="completeItem(event)" class="complete" data-id="${data.id}">${data.isCompleted ? 'Undo' : 'Complete'}</button>
            </div>
            </div>
            </div>`; // 补全原来缺失的闭合标签
  }).join('') // 去掉数组转字符串时自动生成的多余逗号
  // 提交后清空所有输入框
  el.input.value = "";
  el.date.value = "";
  el.time.value = "";
}

3. 实现编辑功能的本地存储同步

修改editItem函数,点击Save保存时,把输入的新内容更新到taskList对应项,再同步存储到localStorage:

function editItem(event) {
  const editBtn = event.target;
  const taskId = editBtn.dataset.id;
  const editEl = editBtn.closest(".task");
  const taskInput = editEl.querySelector(".new-task-created");
  const dateInput = editEl.querySelector(".due-date");
  const timeInput = editEl.querySelector(".due-time");

  if (editBtn.innerHTML.toLowerCase() === "edit"){
    taskInput.removeAttribute("readonly");
    dateInput.removeAttribute("readonly");
    timeInput.removeAttribute("readonly");
    taskInput.focus();
    editBtn.innerHTML = "Save";
  } else {
    // 定位要更新的任务条目
    const targetTask = taskList.find(item => item.id === taskId);
    if(targetTask) {
      // 更新内存中的任务数据
      targetTask.taskNew = taskInput.value.trim();
      targetTask.taskDate = dateInput.value;
      targetTask.taskTime = timeInput.value;
      // 同步到localStorage
      storeList();
    }
    taskInput.setAttribute("readonly", "readonly");
    dateInput.setAttribute("readonly", "readonly");
    timeInput.setAttribute("readonly", "readonly");
    editBtn.innerHTML = "Edit";
    // 重新渲染保证数据和页面显示一致
    renderList();
  }
}

4. 实现标记完成的本地存储同步

修改completeItem函数,点击按钮时切换任务的完成状态,同步存储到localStorage:

function completeItem(event) {
  const completeBtn = event.target;
  const taskId = completeBtn.dataset.id;
  const targetTask = taskList.find(item => item.id === taskId);
  if(targetTask) {
    // 切换完成/未完成状态
    targetTask.isCompleted = !targetTask.isCompleted;
    // 同步到localStorage
    storeList();
    // 重新渲染自动更新样式
    renderList();
  }
}

已完成任务分栏实现(可选)

如果要把未完成、已完成任务分开显示,先修改HTML添加两个列表容器:

<h3>待完成</h3>
<div class="list pending-list"></div>
<h3>已完成</h3>
<div class="list completed-list"></div>

然后修改JS里的元素选择器,把原来的list拆成两个:

const el = {
  form: document.querySelector(".form"),
  input: document.querySelector(".user-input"),
  pendingList: document.querySelector(".pending-list"),
  completedList: document.querySelector(".completed-list"),
  date: document.querySelector(".date"),
  time: document.querySelector(".time"),
};

最后修改renderList,分别渲染两类任务,可以抽离模板减少重复代码:

// 抽离单条任务的渲染模板
function getTaskTemplate(data) {
  const lineStyle = data.isCompleted ? 'text-decoration: line-through;' : '';
  return `<div class="task">
            <div class="task-content">
                <div class="task" data-id="${data.id}">
                <input class="new-task-created" value="${data.taskNew}" readonly style="${lineStyle}">
                <input class="due-date" type="date" value="${data.taskDate}" readonly style="${lineStyle}">
                <input class="due-time" type="time" value="${data.taskTime}" readonly style="${lineStyle}">
            </div>
            <div class="action-buttons">
                <button onclick="editItem(event)" class="edit" data-id="${data.id}">Edit</button>
                <button onclick="deleteItem(event)" class="delete" data-id="${data.id}">Delete</button>
                <button onclick="completeItem(event)" class="complete" data-id="${data.id}">${data.isCompleted ? 'Undo' : 'Complete'}</button>
            </div>
            </div>
            </div>`;
}

function renderList() {
  // 拆分未完成、已完成任务
  const pendingTasks = taskList.filter(item => !item.isCompleted);
  const completedTasks = taskList.filter(item => item.isCompleted);
  // 分别渲染到对应容器
  el.pendingList.innerHTML = pendingTasks.map(getTaskTemplate).join('');
  el.completedList.innerHTML = completedTasks.map(getTaskTemplate).join('');
  // 清空输入框
  el.input.value = "";
  el.date.value = "";
  el.time.value = "";
}

内容的提问来源于stack exchange,提问作者Sheldon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 00:12:27