Vanilla JavaScript渲染对象数组报错:Cannot read properties of null
问题分析与修复方案
错误原因
- 提前获取不存在的DOM元素:代码开头定义的
nameEl、jobEl、task、deleteBtn变量,试图获取页面初始加载时不存在的元素(这些元素是点击按钮后才渲染生成的),所以getElementById返回null,触发Cannot read properties of null错误。而且这些变量后续并未使用,属于冗余代码。 - 重复渲染与文档片段滥用:全局的
list文档片段会被重复添加内容,且未清空itemsEl容器,导致多次点击按钮后任务会重复显示。 - 重复ID属性:渲染任务时给每个任务的标题、按钮都用了相同ID(如
name、deleteBtn),违反HTML中ID必须唯一的规则,会导致后续DOM操作混乱。 - 不当的数组遍历方法:遍历数组仅为渲染元素,无需返回新数组,用
forEach比map更贴合场景。
修复后的代码
HTML(无需修改)
<!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"> <link rel="stylesheet" href="./index.css"> <title>Document</title> </head> <body> <div class="container"> <input id="nameInput" placeholder="enter name" > <input id="jobInput" placeholder="enter job" > <input id="taskInput" placeholder="enter task" > <button id="addBtn">Add Task</button> <div class="items"></div> </div> <script src="./index.js"></script> </body> </html>
JavaScript
const data = []; const itemsEl = document.getElementById("items"); const addBtn = document.getElementById("addBtn"); function addItem() { const name = document.getElementById("nameInput").value; const job = document.getElementById("jobInput").value; const task = document.getElementById("taskInput").value; // 过滤空内容,避免添加无效任务 if (!name.trim() || !job.trim() || !task.trim()) return; data.push({ name, job, task }); displayData(); // 清空输入框,提升体验 document.getElementById("nameInput").value = ""; document.getElementById("jobInput").value = ""; document.getElementById("taskInput").value = ""; } function displayData() { // 先清空容器,防止重复渲染 itemsEl.innerHTML = ""; const list = document.createDocumentFragment(); data.forEach(({ name, job, task }, index) => { const itemDiv = document.createElement("div"); itemDiv.className = "task-item"; itemDiv.innerHTML = ` <h1 class="name">${name}</h1> <h3 class="job">${job}</h3> <h4 class="task">${task}</h4> <button class="deleteBtn" data-index="${index}">Delete Task</button> `; list.appendChild(itemDiv); }); itemsEl.appendChild(list); } // 事件委托处理删除操作,无需给每个按钮单独绑定事件 itemsEl.addEventListener("click", (e) => { if (e.target.classList.contains("deleteBtn")) { const index = parseInt(e.target.dataset.index); data.splice(index, 1); displayData(); } }); addBtn.addEventListener("click", addItem);
额外优化说明
- 增加输入内容校验,避免添加空任务。
- 点击添加后自动清空输入框,优化用户体验。
- 使用事件委托处理删除按钮,更高效且避免重复绑定事件。
- 给任务项添加统一类名
task-item,方便后续样式定制。 - 用
data-index存储任务在数组中的索引,简化删除时的定位逻辑。
内容的提问来源于stack exchange,提问作者Otonye Amietubodie
相关产品推荐
相关产品推荐

