问题描述
在项目中遇到一个小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');
window.event? window.event.cancelBubble = true : e.stopPropagation();
}
复制代码
点击label
非input
区域的时候效果如上,这种方法看似实现了我们的需求,只让我们想要的操作触发一次,但是如果你直接点击的是input,那就跪了。
没错,当你直接点击input
的时候只会触发input
的绑定事件,而我们绑定在label上的事件则无人问津。这并不符合预期,所以方案2不太可取,最后采取方案1。