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

开发Todo应用时LocalStorage无法保存输入值,仅存储空对象的问题求助

嘿,我看到你遇到的问题了——把Todo项存到LocalStorage时总是生成空对象{}对吧?这其实是个常见的小坑,咱们一步步来搞定它:

问题根源

你现在尝试用JSON.stringify(para)把DOM元素(也就是你创建的<ul>节点)转成JSON,但JSON没法正确序列化DOM元素,所以转出来的结果就是空对象。你真正需要存储的是Todo项的文本内容,而不是DOM节点本身。

修复后的完整代码

HTML(小优化:把Todo容器改成<ul>更符合语义)

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Todo App</title>
</head>
<body>
 <h2>Todo List</h2>
 <div id="hello">
 <h1>My Todos</h1>
 </div>
 <button id="btn">delete</button>
 <br>
 <br>
 <input type="text" id="txt" placeholder="Add a new todo...">
 <button type="submit" id="sub">+</button>
 <!-- 改成ul作为列表容器,语义更规范 -->
 <ul id="kno"></ul>
 <script src="main.js"></script>
</body>
</html>

JavaScript(核心逻辑修改)

// 初始化Todo数组:优先从LocalStorage读取,没有的话设为空数组
let todos = JSON.parse(localStorage.getItem('todos')) || [];

const todoContainer = document.getElementById('kno')
const inputBox = document.getElementById('txt')

// 添加Todo的逻辑
document.getElementById('sub').addEventListener('click',()=>{
  const todoText = inputBox.value.trim();
  if (!todoText) return; // 空内容不添加,避免无效Todo
  
  // 创建单个Todo项的DOM元素(用li,符合列表项语义)
  const todoItem = document.createElement('li')
  todoItem.innerText = todoText
  todoContainer.appendChild(todoItem)
  inputBox.value = '' 
  
  // 更新Todo数组并存入LocalStorage
  todos.push(todoText);
  localStorage.setItem('todos', JSON.stringify(todos));
  
  // 绑定标记完成事件
  bindTodoEvents(todoItem, todoText);
})

// 页面加载时,渲染LocalStorage里已存的Todo项
window.addEventListener('load', () => {
  todos.forEach(todoText => {
    const todoItem = document.createElement('li')
    todoItem.innerText = todoText
    todoContainer.appendChild(todoItem)
    bindTodoEvents(todoItem, todoText);
  })
})

// 封装Todo项的事件绑定逻辑,避免重复代码
function bindTodoEvents(todoItem, todoText) {
  // 单击标记完成(可切换状态)
  todoItem.addEventListener('click', ()=>{
    todoItem.style.textDecoration = todoItem.style.textDecoration === 'line-through' ? 'none' : 'line-through';
    // (可选:如果需要保存完成状态,把Todo改成对象存储,比如{text: '买牛奶', completed: false})
  })
  
  // 双击删除
  todoItem.addEventListener('dblclick', ()=>{
    todoContainer.removeChild(todoItem)
    // 从数组中删除对应项,再更新LocalStorage
    todos = todos.filter(item => item !== todoText);
    localStorage.setItem('todos', JSON.stringify(todos));
  })
}

关键改进点

  • 存储文本而非DOM元素:现在存的是用户输入的文本内容,转JSON完全没问题。
  • 用数组存储多个Todo:避免每次添加新Todo覆盖之前的内容。
  • 页面加载时自动渲染:刷新页面后之前添加的Todo不会丢失。
  • 语义化HTML:把Todo容器改成<ul>,列表项用<li>,符合HTML规范。
  • 封装重复逻辑:把事件绑定抽成函数,减少代码冗余。

如果之后想保存Todo的完成状态,只需要把数组里的字符串改成对象(比如{text: '买牛奶', completed: false}),序列化和反序列化的时候处理一下就行啦~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 15:42:39