如何在TodoList中通过e.target定位带唯一ID的生成Div并修改样式
解决TodoList多任务下Checkbox联动Input样式问题
问题背景
开发TodoList时,添加任务会生成带唯一ID的Div,需要实现:勾选任务内的Checkbox时,修改对应Input的背景色;取消勾选时恢复原状。原方案在多Checkbox场景下失效,现需无需知晓具体ID的定位方法,比如结合e.target.parentElement的DOM相对定位,或data-id配合querySelector的实现方式。
解决方案
方法1:DOM相对定位(无需额外ID/标识)
利用事件委托特性,点击Checkbox时,通过e.target获取当前触发事件的Checkbox,再通过DOM层级关系找到同任务内的Input。因为Checkbox和Input在同一个<li>元素下,直接通过父元素或兄弟元素定位即可:
function completeItem(e) { // 仅处理Checkbox的点击事件 if (!e.target.matches('.checkbox')) return; // 找到当前任务项内的Input const todoInput = e.target.closest('.todo-item').querySelector('.generated-input'); // 根据勾选状态切换样式类 if (e.target.checked) { todoInput.classList.add('test'); } else { todoInput.classList.remove('test'); } }
方法2:data-id关联标识
在生成任务元素时,给Checkbox和对应Input添加相同的data-id属性,后续通过该属性关联两者:
- 修改
addToDoItem函数中生成li的代码:
li.innerHTML = ` <input type="checkbox" class="checkbox" name="checkbox" data-id="${randomId}"> <input type="text" class="generated-input" value="${toDoInput.value}" disabled="disabled" data-id="${randomId}">`;
- 修改
completeItem函数:
function completeItem(e) { if (!e.target.matches('.checkbox')) return; const taskId = e.target.dataset.id; // 通过data-id找到对应Input const todoInput = document.querySelector(`.generated-input[data-id="${taskId}"]`); if (e.target.checked) { todoInput.classList.add('test'); } else { todoInput.classList.remove('test'); } }
完整修改后的JS代码
const addButton = document.querySelector(".todo-button"); const toDoInput = document.querySelector(".todo-input"); const toDoUi = document.querySelector(".todo-list"); // 修正原代码选择器错误 const editBtn = document.querySelector(".edit-btn"); addButton.addEventListener("click", addToDoItem); toDoUi.addEventListener("click", deleteItem); toDoUi.addEventListener("click", completeItem); toDoUi.addEventListener("click", editItem); let randomId = ""; function getRandomId() { randomId = new Date().getTime().toString(); } getRandomId(); function addToDoItem(event) { event.preventDefault(); const div = document.createElement("div"); div.classList.add("todo"); const li = document.createElement("li"); // 方法2使用下面的innerHTML,方法1保留原代码即可 // li.innerHTML = ` // <input type="checkbox" class="checkbox" name="checkbox" data-id="${randomId}"> // <input type="text" class="generated-input" value="${toDoInput.value}" disabled="disabled" data-id="${randomId}">`; li.innerHTML = ` <input type="checkbox" class="checkbox" name="checkbox" id="${randomId}"> <input type="text" class="generated-input" value="${toDoInput.value}" disabled="disabled">`; li.classList.add("todo-item"); div.append(li); getRandomId(); const editButton = document.createElement("button"); editButton.classList.add("edit-btn"); editButton.innerText = "Edit"; div.append(editButton); const deleteButton = document.createElement("button"); deleteButton.classList.add("delete-btn"); deleteButton.innerText = `Delete`; div.append(deleteButton); toDoUi.append(div); toDoInput.value = ""; } function deleteItem(e) { if (e.target.classList[0] === "delete-btn") { const todo = e.target.parentElement; todo.remove(); } } function editItem(e) { if (e.target.classList[0] === "edit-btn") { const todoItem = e.target.parentElement; const input = todoItem.querySelector(".generated-input"); input.disabled = false; input.focus(); e.target.classList.add("hideBtn"); const saveButton = document.createElement("button"); saveButton.className = "save-btn"; saveButton.innerText = "Save"; todoItem.firstChild.append(saveButton); // 直接绑定到当前saveButton,避免多任务时事件冲突 saveButton.addEventListener("click", function saveItem() { input.disabled = true; saveButton.remove(); e.target.classList.remove("hideBtn"); }); } } // 方法1的completeItem实现(替换为方法2则注释此段,启用下方方法2代码) function completeItem(e) { if (!e.target.matches('.checkbox')) return; const todoInput = e.target.closest('.todo-item').querySelector('.generated-input'); if (e.target.checked) { todoInput.classList.add('test'); } else { todoInput.classList.remove('test'); } } // 方法2的completeItem实现 // function completeItem(e) { // if (!e.target.matches('.checkbox')) return; // const taskId = e.target.dataset.id; // const todoInput = document.querySelector(`.generated-input[data-id="${taskId}"]`); // if (e.target.checked) { // todoInput.classList.add('test'); // } else { // todoInput.classList.remove('test'); // } // }
说明
- 方法1更简洁,依赖现有DOM结构关联性,无需额外添加标识,适合当前场景;
- 方法2通过
data-id明确关联元素,结构更清晰,适配复杂DOM场景; - 原代码中
completeItem使用document.querySelector会选中页面第一个匹配元素,导致多任务失效,改为基于事件目标的相对定位即可解决问题。
内容的提问来源于stack exchange,提问作者Axnyc
相关产品推荐
相关产品推荐

