为何点击事件仅对每隔一个li元素生效?
待办事项列表点击响应异常:原因与解决办法
问题原因
每次点击提交按钮添加新待办时,你都会遍历所有已存在的<li>,给它们重复绑定点击事件。比如:
- 第一次添加li:给这个li绑定1次点击事件,点击正常切换删除线
- 第二次添加li:遍历所有2个li,给每个都再绑1次事件。此时第一个li有2个点击事件,点击时会先触发加删除线,紧接着触发取消删除线,看起来就像没反应;而新添加的第二个li只有1次绑定,所以能正常工作
- 以此类推,旧li的事件绑定次数会越来越多,点击时多次触发反向操作,表现为“每隔一个li没反应”
另外还有个小问题:点击<li>里的删除按钮时,e.target会指向按钮,导致按钮也被加上删除线,这不是预期行为。
解决方案
方案1:仅给新创建的li绑定事件(简单修正)
不用每次提交都遍历所有li,只给刚创建的newTodo绑定一次点击事件,同时判断点击的目标是不是li的文本部分,避免按钮被误操作:
const ul = document.querySelector('ul') const submitTodo = document.querySelector('input[type="submit"]') const addTodo = document.querySelector('input[type="text"]') submitTodo.addEventListener('click', function(e) { e.preventDefault() // 空内容不添加 if (!addTodo.value.trim()) return let newTodo = document.createElement('li') newTodo.innerText = addTodo.value let newTodoRemove = document.createElement('button') newTodoRemove.innerText = 'Remove To-do' ul.appendChild(newTodo) newTodo.appendChild(newTodoRemove) // 只给新创建的li绑定一次事件 newTodo.addEventListener('click', function(e) { // 点击的是按钮的话,不执行删除线逻辑 if (e.target.tagName === 'BUTTON') return // 切换删除线样式,用getComputedStyle判断更准确(避免内联样式未设置的情况) const currentStyle = window.getComputedStyle(this) if (currentStyle.textDecoration.includes('line-through')) { this.style.textDecoration = 'none' } else { this.style.textDecoration = 'line-through' } }) // 清空输入框 addTodo.value = '' })
方案2:事件委托(更优方案)
把点击事件绑定在父元素<ul>上,利用事件冒泡机制,不管后续添加多少li,都能响应点击。这种方式不用每次添加元素都绑定事件,性能更优:
const ul = document.querySelector('ul') const submitTodo = document.querySelector('input[type="submit"]') const addTodo = document.querySelector('input[type="text"]') // 事件委托:绑定在ul上,处理所有li的点击 ul.addEventListener('click', function(e) { // 只处理li元素的点击 const li = e.target.closest('li') if (!li) return // 点击的是按钮的话,执行删除逻辑 if (e.target.tagName === 'BUTTON') { li.remove() return } // 切换删除线样式 const currentStyle = window.getComputedStyle(li) li.style.textDecoration = currentStyle.textDecoration.includes('line-through') ? 'none' : 'line-through' }) submitTodo.addEventListener('click', function(e) { e.preventDefault() if (!addTodo.value.trim()) return let newTodo = document.createElement('li') newTodo.innerText = addTodo.value let newTodoRemove = document.createElement('button') newTodoRemove.innerText = 'Remove To-do' ul.appendChild(newTodo) newTodo.appendChild(newTodoRemove) addTodo.value = '' })
额外优化点
- 添加了空内容判断,避免创建空的li
- 用
window.getComputedStyle判断样式,比直接读e.target.style更准确(因为style只能读取内联样式,浏览器默认样式或外部样式表的样式读不到) - 方案2里给删除按钮添加了删除li的功能,更符合待办列表的需求
内容的提问来源于stack exchange,提问作者Vincent Schultz
相关产品推荐
相关产品推荐

