
本文详解在 Todo 列表应用中,因误用 span.value 导致 localStorage 存储为 NULL 的根本原因,并提供基于 innerHTML(或更优的 textContent)的修复方案,确保任务文本准确保存与恢复。
本文详解在 todo 列表应用中,因误用 `span.value` 导致 localstorage 存储为 `null` 的根本原因,并提供基于 `innerhtml`(或更优的 `textcontent`)的修复方案,确保任务文本准确保存与恢复。
在构建基于浏览器本地存储的 Todo 应用时,一个常见却易被忽视的错误是:试图通过 .value 属性读取 元素的内容。由于 是纯内容容器元素(phrasing content),它不具有 value 属性——该属性仅存在于表单控件(如 、
✅ 正确做法:使用 textContent(推荐)或 innerHTML
应改用语义更清晰、性能更优且安全的 textContent 属性来提取纯文本内容:
function save() { const liItems = document.queryselectorAll('li span'); const data = []; liItems.forEach(span => { data.push(span.textContent); // ✅ 安全、高效、无 HTML 注入风险 }); localStorage.setItem('todoItems', JSON.stringify(data)); console.log('Saved to localStorage:', data); }
? 为什么推荐 textContent 而非 innerHTML?
- textContent 仅返回标签内的纯文本(自动转义 HTML),避免 xss 风险;
- innerHTML 会返回原始 HTML 字符串(如含 标签则一并返回),在 Todo 场景中通常不需要;
- textContent 性能更好,且语义上更符合“获取待办事项文字”的意图。
? 完整修复后的关键逻辑(含初始化优化)
以下是整合修复点后的精简可靠版本(已修正变量作用域、空值处理及执行时机):
const iteminput = document.querySelector('#item'); const toDoList = document.querySelector('ul'); // 添加新任务 const addToDo = (text = '') => { if (!text.trim()) return; // 忽略空输入 const li = document.createElement('li'); li.innerHTML = ` <span>${escapeHtml(text)}</span> <i class="fas fa-times"></i> `; toDoList.appendChild(li); // 点击切换完成态 li.addEventListener('click', () => li.classList.toggle('dark')); // 删除按钮 li.querySelector('i').addEventListener('click', () => li.remove()); save(); // 实时保存 }; // HTML 转义(防御 XSS,尤其配合 innerHTML 使用) const escapeHtml = (str) => str .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, '''); // 保存所有 span 文本到 localStorage const save = () => { const spans = document.querySelectorAll('li span'); const data = Array.from(spans).map(span => span.textContent); localStorage.setItem('todoItems', JSON.stringify(data)); }; // 页面加载时恢复数据 document.addEventListener('domContentLoaded', () => { const saved = localStorage.getItem('todoItems'); if (saved) { try { const items = JSON.parse(saved); items.forEach(item => addToDo(item)); } catch (e) { console.warn('Failed to parse localStorage data:', e); localStorage.removeItem('todoItems'); // 清理损坏数据 } } }); // 回车提交 itemInput.addEventListener('keyup', (e) => { if (e.key === 'Enter') { addToDo(itemInput.value); itemInput.value = ''; } });
⚠️ 注意事项与最佳实践
- 避免全局变量:原代码中 data = [] 是隐式全局变量,应声明为 const data = [] 或直接内联使用(如示例所示);
- DOMContentLoaded 替代 IIFE:使用事件监听替代立即执行函数,确保 DOM 已就绪;
- 异常防护:JSON.parse() 必须包裹 try…catch,防止 localStorage 数据损坏导致脚本中断;
- 空值校验:在 addToDo() 中检查 text.trim(),避免生成空
- ;
- XSS 防御:若用户输入可能含 HTML,务必对插入内容做转义(如 escapeHtml 函数),切勿直接拼接未过滤的用户输入到 innerHTML。
通过以上修正,你的 Todo 应用将稳定地将真实任务文本存入 localStorage,并在刷新后精准还原——不再出现 “null” 占位符问题。