阻止冒泡与捕获,取消默认事件一次搞懂,看这个就够了
一、EventTarget 事件目标的查找方式(冒泡与捕获)
冒泡与捕获:
冒泡事件
事件默认是由下往上的冒泡执行方式。以点击事件为例,当我们点击子元素也能触发父元素及以上元素的点击事件。事件执行顺序由下至上,这就是冒泡事件。
捕获事件:
当然还有一种执行方式是由上往下的捕获方式。还是以点击事件为例,当一个子元素绑定了点击事件,我们点击子元素时,父元素及以上的元素绑定的点击事件也会执行。事件的执行顺序时由上至下,这就是捕获事件。
addEventListener(type,listener,useCapture) 简单分析:
type:事件类型
listener:事件监听处理函数
useCapture:设置事件查找方式
false,冒泡事件(默认值)
true, 捕获事件
参数useCapture详解:
useCapture的值决定了事件目标的查找方式是捕获还是冒泡。
当一个元素嵌套另外一个元素,且两个元素都注册了相同的事件。给两个元素设置不同useCapture值(冒泡与捕获),所以发生的冒泡和捕获是两种不同的事件查找方式,事件的查找方式决定了元素是以哪个顺序接收事件。
冒泡与捕获的顺序:
从图可以看出先事件捕获再事件冒泡,事件捕获就是从上至下,事件冒泡就是从下至上。
捕获的过程是从不具体的到具体的,冒泡是从具体的到不具体的。
虽然是捕获优先,但是冒泡事件是传递的默认方式。
代码演示:
<body>
<div id="div1">
这是div1
<div id="div2">
这是div2
<div id="div3">这是div3</div>
</div>
</div>
<script>
let div1 = document.getElementById('div1');
let div2 = document.getElementById('div2');
let div3 = document.getElementById('div3');
div1.addEventListener('click',function(){
console.log("这是div1的点击事件");
},false);
div2.addEventListener('click',function(){
console.log("这是div2的点击事件");
},false);
div3.addEventListener('click',function(){
console.log("这是div3的点击事件");
},false);
</script>
</body>
当我们点击div3,如下从控制台结果可以看出,这里发生就是冒泡事件。
还是点击div3,我们将div1.addEventListener第三个参数改为true,如下可以看出div1最先执行,说明捕获优先于冒泡。
这里看完一定要敲一下,我并没有列举所有的情况,其余的情况留给你们去尝试再总结(能理解上面的就够了,真正编码不会很复杂)。
如上就是我对事件目标查找的两种机制冒泡与捕获理解。
二、事件代理机制
利用事件冒泡完成事件代理机制:
<ul>
<li>列表1</li>
<li>列表2</li>
</ul>
当我们要给如上列表中的li都绑定一个点击事件点击获取li中的数据,一般是利用for遍历元素绑定点击事件。
let lis = document.querySelectorAll('li');
for (let i = 0; i < lis.length; i++) {
lis[i].addEventListener('click', function () {
console.log(this.innerHTML);
});
}
但是当我们元素过多时这样操作会影响代码性能,所以我们可以利用冒泡机制来完成事件代理。就是将事件绑定到父元素身上。
关于事件对象的知识点:
当我们通过EventTarget.addEventListener(type,listener,useCapture);绑定一个事件,事件监听处理函数(listener)中的第一个参数为事件对象。事件对象包含了这个事件的详细信息,比如这个对象中包含了事件源,事件id,事件类型,事件绑定的元素,事件触发时点击的位置等等。
<body>
<ul>
<li>列表1</li>
<li>列表2</li>
</ul>
<script>
let ul = document.querySelector('ul');
//我们可以通过事件对象e中的target属性可以访问到事件源(也就事件的触发元素)
ul.addEventListener('click',function(e){
console.log(e.target.innerHTML);
},false);
</script>
</body>
依次点击列表1与列表2:
总结:通过上面代码我们知道了e.target可以实现事件委托,事件委托是通过事件冒泡(或事件捕获)给父元素添加事件, e.target可以访问的是触发事件的元素(事件源)。所以事件委托就是当事件触发时,把要做的事委托给父元素来处理
三、e.target与e.currentTarget的区别:
e.target 指向的是触发事件监听的对象(事件源)。
e.currentTarget 指向添加监听事件的对象(绑定事件的dom元素)。
四、阻止冒泡与捕获
为什么要阻止冒泡或捕获?
点击当前元素时以冒泡的方式传递事件如果上级元素绑定了同样的事件,就会因为冒泡传递导致触发。同样捕获的过程中,也会触发与当前元素绑定的相同事件的上级。只是触发顺序不同。
事件代理一般使用的冒泡,当然阻止冒泡一般不会影响事件代理,因为顺序问题只会影响捕获事件,这也是为什么都使用冒泡实现事件代理机制。
阻止冒泡或捕获的方法
这里我不考虑兼容性问题,我相信不久将来兼容性可以得到解决。
阻止冒泡w3c推介的方法是event.stopPropagation(),顾名思义停止传播,他是事件对象(event)的方法,此方法是阻止目标元素的继续冒泡(或捕获)。
event.stopPropagation()阻止冒泡:
<body>
<div id="div1">
这是div1
<div id="div2">
这是div2
<div id="div3">这是div3</div>
</div>
</div>
<script>
let div1 = document.getElementById('div1');
let div2 = document.getElementById('div2');
let div3 = document.getElementById('div3');
div1.onclick = function (e) {
alert('div1');
}
div2.onclick = function (e) {
e.stopPropagation();
alert('div2');
}
div3.onclick = function (e) {
alert('div3');
}
</script>
</body>
上面代码默认都是冒泡事件,我们点击div3会依次弹出’div3’与’div2’,为什么没有弹出’div1’这是因为e.stopPropagation();阻止了目标元素的事件继续冒泡到上级。如果每个点击事件都加上了e.topPropagation就不会出现多弹窗的情况。
event.stopPropagation()阻止捕获:
<body>
<div id="div1">
这是div1
<div id="div2">
这是div2
<div id="div3">这是div3</div>
</div>
</div>
<script>
let div1 = document.getElementById('div1');
let div2 = document.getElementById('div2');
let div3 = document.getElementById('div3');
div1.addEventListener('click',function(e){
console.log('div1');
},true);
div2.addEventListener('click',function(e){
console.log('div2');
e.stopPropagation();
},true);
div3.addEventListener('click',function(e){
console.log('div3');
},true);
</script>
</body>
当我们点击div2会依次弹出’div1’与’div2’,这也是因为在div2事件中我们设置了e.stopPropagation(),阻塞了目标元素的事件继续向下捕获。
event.target == event.currentTarget:
div.addEventListener('click',function(e){
if(event.target == event.currentTarget){
//需要执行的代码
}
});
此方法不过多解释用的不多,如果你理解了上面的内容,这个方法也能理解。
五、为什么要使用addEventListener()
从上面代码不难看出addEventListener()有如下的优点(以下是MDN的原话):
addEventListener() 是 W3C DOM 规范中提供的注册事件监听器的方法。它的优点包括:
它允许给一个事件注册多个监听器。 特别是在使用AJAX库,JavaScript模块,或其他需要第三方库/插件的代码。
它提供了一种更精细的手段控制 listener 的触发阶段。(即可以选择捕获或者冒泡)。
它对任何 DOM 元素都是有效的,而不仅仅只对 HTML 元素有效。
六、取消默认事件
event.preventDefault()
默认事件指的是<a href="">,<input type="submit">标签这类有默认行为的标签,通过点击可以跳转或提交。我们给这类标签绑定一个点击事件,设置事件对象的preventDefault()方法就可以阻止默认事件的发生。
<body>
<a href="https://www.baidu.com">点击跳转</a>
<script>
let a = document.querySelector('a');
addEventListener('click',function(e){
e.preventDefault();
})
</script>
</body>
那么我们如何才能知道一个标签是否有默认事件,打印事件对象的cancelable属性,通过事件执行就可以知道e.cancelable的结果,如果为false表示有默认事件,true则没有。
return false;
事件执行函数中设置return false取消默认事件,但此方法不常用。
————————————————
版权声明:本文为博主「寸头男生」的原创文章