Chrome.storage.sync存取数组 待办扩展删项与加载异常
问题根因
- 代码存在重复定义:
saveList、getList被定义了两次,按钮点击、回车、表单阻止默认提交的事件也重复绑定了两次,后定义的函数会覆盖前序逻辑,直接导致加载、存储逻辑混乱 - 索引作为ID的设计缺陷:新建任务时用当前数组长度作为元素ID,删除元素后数组索引会整体偏移,但DOM元素上绑定的旧ID不会更新,调用
splice按旧ID删元素必然会删错位置 - 存储结构设计缺陷:目前只往存储里存了纯文本字符串,没有存任务完成状态,代码里尝试给数组里的字符串值设置
style.textDecoration属性会直接触发类型错误 - 加载逻辑缺参数:后定义的
getList函数调用createListAfterLoad时只传了文本内容,没传ID参数,导致函数内拿到的id_number为undefined,转数值后为NaN,元素ID异常 - 边界场景未处理:首次打开扩展时存储内无
saved_list字段,直接遍历result.saved_list会触发空引用报错
修复方案
- 清理所有重复的函数定义、重复的事件绑定
- 调整存储结构,每个任务存为带唯一ID、文本内容、完成状态的对象,用时间戳生成全局唯一任务ID,不再依赖数组索引
- 删除任务时通过唯一ID过滤数组,避免索引偏移导致的删错问题
- 加载存储数据前先判空,无历史数据时默认初始化为空数组
- 完成状态随任务对象一起存储,加载时同步恢复样式
修复后完整popup.js代码
const form = document.querySelector('#task-form'); const input = document.querySelector('#task-input'); const list_el = document.querySelector('#tasks'); const submitButton = document.querySelector('#submit-button'); let task_list = []; function inputLength(){ return input.value.length; } function addTaskClick(){ if (inputLength() > 0) { createList(); } } function addTaskEnter(event) { if (inputLength() > 0 && event.which ===13) { createList(); } } // 抽离通用的任务元素创建逻辑,新增和加载复用 function buildTaskElement(taskObj) { const task_el = document.createElement('div'); task_el.className = 'task'; task_el.dataset.taskId = taskObj.id; const task_complete_el = document.createElement('button'); task_complete_el.className = 'actions'; task_complete_el.type = 'button'; const complete_button_style = document.createElement('i'); complete_button_style.classList.add('fa', 'fa-solid', 'fa-check'); task_complete_el.appendChild(complete_button_style); task_el.appendChild(task_complete_el); const task_input_el = document.createElement('input'); task_input_el.className = 'text'; task_input_el.type = 'text'; task_input_el.value = taskObj.text; // 恢复完成状态 if (taskObj.completed) { task_input_el.style.textDecoration = 'line-through'; } task_el.appendChild(task_input_el); const task_delete_el = document.createElement('button'); task_delete_el.className = 'actions'; task_delete_el.type = 'button'; const del_button_style = document.createElement('i'); del_button_style.classList.add('fa', 'fa-solid', 'fa-times'); task_delete_el.appendChild(del_button_style); task_el.appendChild(task_delete_el); // 绑定删除事件 task_delete_el.addEventListener('click', () => { list_el.removeChild(task_el); // 按唯一ID过滤数组,避免索引偏移问题 task_list = task_list.filter(item => item.id !== taskObj.id); saveList(task_list); }); // 绑定完成状态切换事件 task_complete_el.addEventListener('click', () => { const targetTask = task_list.find(item => item.id === taskObj.id); if (!targetTask) return; if (task_input_el.style.textDecoration !== 'line-through') { task_input_el.style.textDecoration = 'line-through'; targetTask.completed = true; } else { task_input_el.style.textDecoration = 'none'; targetTask.completed = false; } saveList(task_list); }) return task_el; } function createList() { const taskObj = { id: Date.now(), // 用时间戳生成唯一ID text: input.value.trim(), completed: false }; const task_el = buildTaskElement(taskObj); list_el.appendChild(task_el); task_list.push(taskObj); saveList(task_list); input.value = ""; } function createListAfterLoad(taskObj) { const task_el = buildTaskElement(taskObj); list_el.appendChild(task_el); } function saveList(list) { chrome.storage.sync.set({saved_list: list}, function() { console.log('存储更新', list); }); } function getList() { chrome.storage.sync.get(['saved_list'], function(result) { // 首次加载无数据时初始化为空数组 task_list = result.saved_list || []; // 清空现有列表避免重复渲染 list_el.innerHTML = ''; task_list.forEach(taskObj => { createListAfterLoad(taskObj); }); }); } // 事件绑定只保留一次 submitButton.addEventListener("click", addTaskClick); input.addEventListener("keypress", addTaskEnter); form.addEventListener('submit', (e) => { e.preventDefault(); }); document.addEventListener('DOMContentLoaded', getList);
注意:按钮的
type从submit改成了button,避免点击按钮时意外触发表单提交逻辑,和已有的preventDefault逻辑配合更稳定。
内容的提问来源于stack exchange,提问作者Lulu
相关产品推荐
相关产品推荐

