在JS中经常需要阻止元素的默认事件。而阻止默认事件的方法都是使用事件对象的preventDefault()
方法或者在函数中return false
。在最近一次开发中使用preventDefault()
方法的时候遇到一个问题,现在才想/猜明白原因,场景是这样的:
<a href="https://www.csdn.net" target="_blank"><p>点我跳转</p></a>
document.querySelector('p').addEventListener('click', function (ev) {ev.preventDefault()
})
document.querySelector('a').addEventListener('click', function (ev) {console.log('事件冒泡到a标签')
})
结果是链接没有跳转。当时觉得莫名其妙,阻止默认事件是在点击p元素时候阻止的,也只是阻止了默认事件,并没有阻止事件冒泡,事件还是冒泡到了a元素,但是却没有触发a元素跳转,就像在a元素上阻止了默认事件一样。
要理解这个现象首先要知道在p元素和a元素的click事件处理函数中捕获的ev对象是同一个对象。
注:如果监听的事件不同捕获的事件对象自然不同。
修改上面的脚本如下:
var eve = null
document.querySelector('p').addEventListener('click', function (ev) {eve = ev
})
document.querySelector('a').addEventListener('click', function (ev) {console.log(eve === ev) // 可以看出来输出了 true
})
所以在事件冒泡过程中只要有一个处理函数阻止了默认事件,那么当事件冒泡到祖先元素的时候也是被阻止了默认事件的事件对象。
还可以通过事件对象上的一个属性defaultPrevented
看出来这个事件对象执行过preventDefault()
方法。
document.querySelector('p').addEventListener('click', function (ev) {console.log('执行之前', ev.defaultPrevented) // 输出falseev.preventDefault()console.log('执行之后', ev.defaultPrevented) // 输出true
})
document.querySelector('a').addEventListener('click', function (ev) {console.log(ev.defaultPrevented) // 输入 true
})
事件传递过程
高程上介绍事件的一章中介绍到,事件传播分为三个阶段,首先是捕获,然后是处于目标,然后是冒泡。
在目标元素上绑定事件会发现,addEventListener第三个参数为true绑定的函数并不会在为false绑定的函数之前执行,而执行顺序只会和绑定顺序有关。
所以为什么在事件处理函数中打印事件对象的currentTarget属性在控制台看不到值呢?因为当我们在控制台看的时候事件对象已经不是事件处理函数console.log那一刻的对象了,在向上冒泡的过程中被修改了。