如何正确从 DOM 中提取 span 文本并持久化到 localStorage

4次阅读

如何正确从 DOM 中提取 span 文本并持久化到 localStorage

本文详解在 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, '&#039;');  // 保存所有 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” 占位符问题。

text=ZqhQzanResources