问题描述
在项目中遇到一个小 bug--label 标签上绑定了一个返回一层路由的点击事件, 然而每次点击都会返回两层!!
经调试发现, label 标签中包裹 input, 而事件绑定在 label 标签中时, 点击 label 区域, 事件会执行两次.
问题测试
- <label onclick="labelConsole()">
- <input type="checkbox" onclick="inputConsole()"> 勾选协议
- </label>
- function labelConsole (){
- console.log('label_click 这是我们想要的操作')
- }
- function inputConsole (){
- console.log('input_click')
- }
点击 label 区域 (不直接点击 input 区域):label 上的事件被触发执行一次, 同时子元素 input 本身也绑定有 click 事件, 触发后又冒泡传递给 label, 又触发了一次 label 绑定事件.
直接点击 input 区域: 触发 input 绑定事件后又冒泡传递给 label, 触发了 label 的绑定事件.
分析原因
元素默认绑定 click 事件
一些元素如 < a>,<button>,<input > 本身就默认绑定了 click 事件, 即使你不绑定, click 事件发生时他们也会接收到.
label 标签的扩展性
它把所包含的 input 的用户交互区域扩展了, 注意的是 label 和所包含的 input 都开始绑定默认事件, 此时会发生事件冒泡现象. 在 label 上的 click 事件的处理函数会触发 2 次就是由于: 第一次是 label 自己接收到事件, 执行处理函数, 第二次是 input 接受到事件后冒泡传递给 label, 再次触发处理函数.
解决方案
方案 1: 将原绑定于 label 的事件, 直接绑定于 input 上.
- <label>
- <input type="checkbox" onclick="labelConsole()"> 勾选协议
- </label>
此时, 当点击 label 区域或者直接点击 input 区域, 由于 checkbox 本身有默认 click 监听器, 所以会触发一次我们绑定的事件.
方案 2: 阻止事件冒泡
- <label onclick="labelConsole()">
- <input type="checkbox" onclick="inputConsole()"> 勾选协议
- </label>
- function labelConsole (){
- console.log('label_click 这是我们想要的操作')
- }
- function inputConsole (){
- console.log('input_click');
- Windows.event? Windows.event.cancelBubble = true : e.stopPropagation();
- }
点击 label 非 input 区域的时候效果如上, 这种方法看似实现了我们的需求, 只让我们想要的操作触发一次, 但是如果你直接点击的是 input, 那就跪了.
没错, 当你直接点击 input 的时候只会触发 input 的绑定事件, 而我们绑定在 label 上的事件则无人问津. 这并不符合预期, 所以方案 2 不太可取, 最后采取方案 1.
来源: https://juejin.im/post/5c66752d6fb9a049d37fa191