最近面试被问到关于事件冒泡和捕获的知识,大部分都还记得,栽在了IE上面(IE8-只有冒泡没有捕获,IE9+支持捕获),回来记忆,顺便把一些和事件流有关的内容再梳理一遍。
我的理解:
事件冒泡
顾名思义,气泡从小变大,从冒泡起点到越来越大,所以是从目标事件源扩散出去的,一直到HTML根元素,期间遇到的同类型的事件也被自动触发;(IE和常用浏览器都支持)
事件捕获
从根元素开始,逐渐逐渐缩小范围,直到目标事件源,期间遇到的同类型事件也被自动触发;(除了IE的常用浏览器)
事件流
就是事件的流向,先捕获,再到事件源,最后再冒泡,一共分三个阶段:捕获阶段,目标阶段,冒泡阶段。(W3C标准,当处于目标阶段时事件触发)
事件委托/代理机制
利用冒泡机制来传递,那可以让某个父节点统一处理事件,通过判断事件的发生地(即事件产生的节点),然后做出相应的处理。就是将子元素的事件处理程序绑定到父类上,例如常见的ul>li> a列表标签的写法应用。这样可以减少绑定事件。
在一些标准的浏览器中,如Chrome、Firefox等,支持这两种冒泡方式。而事实上,准确的说,是这两种方式的混合方式。因为W3C采取的就是这两种方式的结合:先从顶级节点开始,将事件向下传递至源节点,再从源节点冒泡至顶节点。
而在IE及Opera(以下说的都是老版本的欧朋,新版本的欧朋经检测是支持事件捕获的)下,是不支持事件捕获的。而这个特点最明显体现在事件绑定函数上。
IE、Opera的事件绑定函数是attachEvent,而Chrome等浏览器则是addEventListener,而后者比前者的参数多了一个——这个参数是一个布尔值,这个布尔值由用户决定,用户若设为true,则该绑定事件以事件捕获的形式参与,若为false则以事件冒泡的形式参与,默认为false;
ele.addEventListener("click",function(e){
e.stopPropagation();
})
// 这样ele的父元素就接收不到事件了
stopPropagation()方法既可以阻止事件冒泡,也可以阻止事件捕获,也可以阻止处于目标阶段
无论事件流中只有捕获还是事件流中只有冒泡,还是说是事件流中既有捕获还有冒泡,event.stopPropagation()都可以阻止事件流的传播顺序。
stopPropagation:防止对事件流中当前节点的后续节点中的所有事件侦听器进行处理。
只要是event.stopPropagation()加在哪里,则到哪里就停止运行,停止捕获或者停止冒泡,简单说是,仍然按照正常的混合机制流程走,只是哪里有e.stopPropagation()则这个流程到哪里就停止了,!!!但是只是阻止当前事件,如果后续还有绑定则会继续触发,也就是说,不是彻底取消click事件。
element.addEventListener('click', function (event) {
event.stopPropagation();
console.log(1);//不会触发
});
element.addEventListener('click', function(event) {
// 会触发
console.log(2);
});
stopImmediatePropagation: 简单说就是当前节点,同事件类型情况下,如果执行了该方法,则当前事件内执行,后续同类型事件不执行。与stopPropagation()有区别;
<!DOCTYPE html>
<html>
<head>
<style>
p { height: 30px; width: 150px; background-color: #ccf; }
div {height: 30px; width: 150px; background-color: #cfc; }
</style>
</head>
<body>
<div>
<p>paragraph</p>
</div>
<script>
const p = document.querySelector('p')
p.addEventListener("click", (event) => {
alert("我是p元素上被绑定的第一个监听函数");//执行
}, false);
p.addEventListener("click", (event) => {
event.stopImmediatePropagation();
alert("我是p元素上被绑定的第二个监听函数");//执行
// 执行stopImmediatePropagation方法,阻止click事件冒泡,
//并且阻止p元素上后续绑定的其他click事件的事件监听函数的执行.
}, false);
p.addEventListener("click",(event) => {
alert("我是p元素上被绑定的第三个监听函数");
// 该监听函数排在上个函数后面,该函数不会被执行
}, false);
document.querySelector("div").addEventListener("click", (event) => {
alert("我是div元素,我是p元素的上层元素");
// p元素的click事件没有向上冒泡,该函数不会被执行
}, false);
</script>
</body>
</html>
可以使用DOM3级新增事件stopImmediatePropagation()方法来阻止事件捕获,那么 stopImmediatePropagation() 和 stopPropagation()的区别在哪儿呢?
后者只会阻止冒泡或者是捕获。 但是前者除此之外还会阻止该元素的其他事件发生,但是后者就不会阻止其他事件的发生(从而需要设置取消默认)。
阻止默认事件
有一些html元素默认的行为,比如说a标签,点击后有跳转动作;form表单中的submit类型的input有一个默认提交跳转事件;reset类型的input有重置表单行为。
e.preventDefault();//非IE
e.returnValue();//IE下阻止默认
阻止事件冒泡
let e = e||window.event;
if(e.stopPropagation){
e.stopPropagation();//其它浏览器
}else{
e.cancelBubble = true;//IE浏览器
}
绑定与解绑
/**
* @Date: 2019-10-16 09:52:56
* @param element:目标元素
* @param eType:事件类型
* @param handle:回调函数
* @param bol:是否捕获
* @author lg
* @desc:事件绑定与解绑
**/
function addEvent(element, eType, handle, bol) {
if (element.addEventListener) {
//如果支持addEventListener (主流浏览器和IE9+)
element.addEventListener(eType, handle, bol);
} else if (element.attachEvent) {
//如果支持attachEvent (IE8-)
element.attachEvent("on" + eType, handle);
} else {
//否则使用兼容的onclick绑定
element["on" + eType] = handle;
//element.onclick = function abc(){ alert('abc'); }
}
}
function removeEvent(element, eType, handle, bol) {
if (element.removeEventListener) {
//如果支持removeEventListener (主流浏览器和IE9+)
element.removeEventListener(eType, handle, bol);
} else if (element.detachEvent) {
//如果支持detachEvent(IE8-)
element.detachEvent("on" + eType, handle);
} else {
//否则使用兼容的onclick绑定
element["on" + eType] = null;
//element.onclick = null;
}
}