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

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会触发空引用报错
修复方案
  1. 清理所有重复的函数定义、重复的事件绑定
  2. 调整存储结构,每个任务存为带唯一ID、文本内容、完成状态的对象,用时间戳生成全局唯一任务ID,不再依赖数组索引
  3. 删除任务时通过唯一ID过滤数组,避免索引偏移导致的删错问题
  4. 加载存储数据前先判空,无历史数据时默认初始化为空数组
  5. 完成状态随任务对象一起存储,加载时同步恢复样式
修复后完整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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 01:09:25