JavaScript新手疑问:DOM元素访问不存在的completed属性原理
为什么DOM元素能访问
completed属性? 核心原因很简单:在JavaScript中,所有DOM元素(比如你代码里的<li>)本质都是JavaScript对象。你可以像给普通JS对象添加自定义属性一样,直接给DOM元素新增属性,这就是clickedEl.completed能被访问的原因。
具体发生的过程:
- 第一次点击
<li>时,clickedEl.completed这个属性还不存在,所以条件clickedEl.completed === true不成立,进入else分支:- 给该
<li>添加删除线样式 - 手动给这个DOM对象新增一个
completed属性,并赋值为true
- 给该
- 第二次点击同一个
<li>时,clickedEl.completed已经存在且值为true,所以进入if分支:- 移除删除线样式
- 这里你代码漏了一步:应该把
clickedEl.completed设回false,否则下次点击逻辑会出错
你的代码存在的关键问题
现在你的todosArray(存在localStorage里)和DOM元素的completed属性是完全脱节的:
- 点击
<li>标记完成时,只修改了DOM元素的属性,没有更新todosArray里对应todoObj的completed值,刷新页面后,之前标记的完成状态会丢失 - 从localStorage读取todo渲染到页面时,没有把
todoObj.completed的状态同步到DOM元素(比如如果原数据是已完成,页面上的<li>应该直接显示删除线,并且给DOM元素加上completed属性)
修正方案(关联DOM和数据)
给每个<li>添加一个data-id属性,关联todosArray里对应对象的索引,这样点击时可以找到对应的数据,同步更新DOM和数组:
// 新增:给todoObj添加id(用数组索引当id即可) function addTodo(item) { const todoObj = { id: todosArray.length, // 用数组长度作为唯一id todo: item, completed: false }; todosArray.push(todoObj); localStorage.setItem('todosArray', JSON.stringify(todosArray)); formInput.value = ''; } // 提交表单时,给li加上data-id form.addEventListener('submit', function (e) { e.preventDefault(); if (formInput.value === '') { alert('Please add a task.'); } else { const newLi = document.createElement('li'); newLi.innerHTML = formInput.value; // 给li添加data-id,关联刚添加的todoObj的id newLi.dataset.id = todosArray.length; ul.append(newLi); const removeBtn = document.createElement('button'); newLi.append(removeBtn); removeBtn.innerHTML = 'Remove'; addTodo(formInput.value); } }); // 点击li时,同步更新DOM和数据 ul.addEventListener('click', function (e) { if (e.target.textContent === 'Remove') { const li = e.target.parentElement; // 删除对应的数组元素 const todoId = parseInt(li.dataset.id); todosArray.splice(todoId, 1); // 重新保存到localStorage localStorage.setItem('todosArray', JSON.stringify(todosArray)); li.remove(); } else if (e.target.tagName === 'LI') { const clickedEl = e.target; const todoId = parseInt(clickedEl.dataset.id); const todoObj = todosArray[todoId]; if (todoObj.completed) { clickedEl.style.textDecoration = 'none'; todoObj.completed = false; clickedEl.completed = false; // 同步DOM属性 } else { clickedEl.style.textDecoration = 'line-through'; todoObj.completed = true; clickedEl.completed = true; // 同步DOM属性 } // 更新localStorage localStorage.setItem('todosArray', JSON.stringify(todosArray)); } }); // 从localStorage读取时,同步DOM状态 if (localStorage.getItem('todosArray')) { todosArray = JSON.parse(localStorage.getItem('todosArray')); for (let i = 0; i < todosArray.length; i++) { const newLi = document.createElement('li'); newLi.textContent = todosArray[i].todo; newLi.dataset.id = i; // 如果原数据是已完成,同步样式和DOM属性 if (todosArray[i].completed) { newLi.style.textDecoration = 'line-through'; newLi.completed = true; } ul.append(newLi); const removeBtn = document.createElement('button'); newLi.append(removeBtn); removeBtn.textContent = 'Remove'; } }
这样修改后,DOM元素的状态和todosArray的数据就完全同步了,刷新页面也不会丢失完成状态。
内容的提问来源于stack exchange,提问作者Kwok
相关产品推荐
相关产品推荐

