
本文解决动态添加的待办事项中,复选框点击后样式仅作用于首个任务的问题;核心在于正确获取当前复选框对应的 <p class="task"> 元素,而非全局查找首个匹配项。
本文解决动态添加的待办事项中,复选框点击后样式仅作用于首个任务的问题;核心在于正确获取当前复选框对应的 `
` 元素,而非全局查找首个匹配项。
在实现动态待办清单时,一个常见陷阱是:每次新增任务后,为所有复选框统一绑定事件监听器,但在事件处理中使用 document.querySelector(".task") —— 该方法始终返回 DOM 中第一个匹配 .task 的元素,导致无论点击哪个复选框,样式都只作用于第一个任务。
✅ 正确做法:基于事件源定位兄弟元素
复选框(<input type="checkbox" class="done">)与其对应的任务文本(<p class="task">)处于同一父容器 <div class="task_container"> 内,且顺序相邻:
<div class="task_container"> <input type="checkbox" class="done"> <p class="task">买牛奶</p> <!-- 紧跟在 checkbox 后 --> </div>
因此,可直接利用 c.nextElementSibling 获取当前复选框的下一个兄弟节点(即其专属的 .task 元素),无需依赖全局查询或索引匹配。
? 修复后的完整代码
const input = document.querySelector("#input");
const container = document.querySelector(".to_dos_container");
input.addEventListener("keyup", (event) => {
if (event.key === "Enter" && input.value.trim()) {
const add = `
<div class="task_container">
<input type="checkbox" class="done">
<p class="task">${input.value}</p>
</div>
`;
container.insertAdjacentHTML("afterbegin", add);
input.value = "";
lineThrough(); // 重新绑定新元素的事件(见下方优化建议)
}
});
function lineThrough() {
const checkboxes = document.querySelectorAll(".done");
checkboxes.forEach((checkbox) => {
// 移除已有监听器,避免重复绑定(关键!)
checkbox.replaceWith(checkbox.cloneNode(true));
const freshCheckbox = container.querySelector(".done:last-child");
freshCheckbox.addEventListener("click", () => {
const task = freshCheckbox.nextElementSibling; // ✅ 精准定位同级 task
if (freshCheckbox.checked) {
task.style.textDecoration = "line-through";
task.style.color = "gray";
} else {
task.style.textDecoration = "none";
task.style.color = "";
}
});
});
}⚠️ 注意:原代码存在严重隐患 —— 每次调用 lineThrough() 都会为所有现有复选框重复添加监听器,导致点击一次触发多次执行。上述修复中通过 cloneNode 替换并仅绑定最新元素,是一种简易解法;更推荐的生产级方案是事件委托(见下文)。
? 推荐进阶方案:事件委托(高效 & 无重复绑定)
避免反复绑定/解绑,将监听器统一挂载在父容器上:
container.addEventListener("change", (e) => {
if (e.target.classList.contains("done")) {
const task = e.target.nextElementSibling;
if (task && task.classList.contains("task")) {
task.style.textDecoration = e.target.checked ? "line-through" : "none";
task.style.color = e.target.checked ? "gray" : "";
}
}
});✅ 优势:
- 仅需绑定一次监听器;
- 自动响应后续动态插入的所有复选框;
- 无内存泄漏与重复绑定风险。
? 总结
- ❌ document.querySelector(".task") 是全局查找,永远只命中第一个;
- ✅ 使用 element.nextElementSibling 或 element.parentElement.querySelector(".task") 实现上下文感知;
- ? 优先采用事件委托管理动态元素事件,兼顾性能与可维护性。

















