设置useCapture=true的点击事件处理器为何在带图片的按钮上不触发
核心误区说明
1. 事件捕获的触发逻辑
你测试div>ul>li结构时没有看到上层元素的事件触发,是因为只有主动给对应元素绑定了捕获模式的同类型事件监听器,才会在捕获阶段执行对应回调,不是开启捕获模式就会自动触发所有父元素的点击事件。
如果要复现你看到的捕获顺序效果,需要给div、ul分别绑定捕获模式的点击事件,示例代码如下:
<div id="d">div <ul id="u">ul <li id="l">li</li> </ul> </div> <script> document.querySelector('#d').addEventListener('click', ()=>console.log('div 捕获阶段触发'), true) document.querySelector('#u').addEventListener('click', ()=>console.log('ul 捕获阶段触发'), true) document.querySelector('#l').addEventListener('click', ()=>console.log('li 捕获阶段触发'), true) document.querySelector('#l').addEventListener('click', ()=>console.log('li 冒泡阶段触发'), false) document.querySelector('#u').addEventListener('click', ()=>console.log('ul 冒泡阶段触发'), false) document.querySelector('#d').addEventListener('click', ()=>console.log('div 冒泡阶段触发'), false) </script>
点击li元素后,控制台输出顺序会严格按照「捕获从外层到内层,冒泡从内层到外层」的规则执行,和你之前看到的描述一致。
2. 带图片按钮点击不生效的原因
核心是你混淆了event.target和event.currentTarget的区别,同时对preventDefault()的作用有误解:
event.target永远指向实际触发事件的最内层DOM元素:点击按钮文本区域时,最内层元素对应button本身,所以event.target.id == "BUTTON"判断成立;点击图片时,最内层元素是<img>,没有id属性,判断自然不通过。- 你设置的
useCapture = true仅代表监听器会在捕获阶段执行,完全不改变event.target的取值,也不会改变事件传递路径。 preventDefault()的作用仅为阻止浏览器默认行为(比如链接跳转、表单提交),不会中断事件传递,要中断传递需要调用event.stopPropagation()。
解决方案
方案1:保留现有事件绑定逻辑,修改判断条件
用closest()方法判断点击元素是否在button内部,不需要给子元素加额外样式:
function working(event) { if (event.target.closest("#BUTTON")) { alert("Its working!") event.preventDefault(); } } document.addEventListener('click', working, true);
方案2:直接给button绑定事件
直接将事件监听器绑定到button元素上,用event.currentTarget获取绑定事件的元素,不管点击按钮内部的哪个子元素都会触发:
document.querySelector('#BUTTON').addEventListener('click', function(e) { alert("Its working!") e.preventDefault() })
方案3:沿用你常用的hack方案
给按钮内的图片添加pointer-events: none样式,让图片不响应鼠标事件,点击图片时event.target会自动穿透到父元素button:
#BUTTON img { pointer-events: none; }
内容的提问来源于stack exchange,提问作者run_the_race
相关产品推荐
相关产品推荐

