详解浏览器事件捕获,冒泡
浏览器事件模型中的过程主要分为三个阶段:捕获阶段、目标阶段、冒泡阶段。
这里先看一下这张经典的图
第三个参数
这里要注意addEventListener
的第三个参数, 如果为true
,就是代表在捕获阶段执行。如果为false
,就是在冒泡阶段进行
空口这么说可能不好理解,咱们来看一下代码。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div id="parent" class="flex-center">
parent
<p id="child" class="flex-center">
child
<span id='son' class="flex-center">
son
<a href="https://www.baidu.com" id="a-baidu">点我啊</a>
</span>
</p>
</div>
</body>
<script type="text/javascript" src="index.js"></script>
<style>
#parent {
background-color: bisque;
width: 700px;
height: 700px;
}
#child {
background-color: chocolate;
width: 500px;
height: 500px;
}
#son {
background-color: crimson;
width: 300px;
height: 300px;
}
.flex-center {
display: flex;
justify-content: center;
align-items: center;
font-size: 20px;
}
</style>
</html>
复制代码
const parent = document.getElementById("parent");
const child = document.getElementById("child");
const son = document.getElementById("son");
// const baidu = document.getElementById("a-baidu");
// baidu.addEventListener('click', function (e) {
// e.preventDefault();
// })
window.addEventListener("click", function (e) {
// e.target.nodeName 指当前点击的元素, e.currentTarget.nodeName绑定监听事件的元素
console.log("window 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);
parent.addEventListener("click", function (e) {
// e.stopPropagation(); 不再派发事件。终止事件在传播过程的捕获、目标处理或起泡阶段进一步传播。调用该方法后,该节点上处理该事件的处理程序将被调用,事件不再被分派到其他节点。
// e.target.nodeName 指当前点击的元素, e.currentTarget.nodeName绑定监听事件的元素
console.log("parent 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);
child.addEventListener("click", function (e) {
console.log("child 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);
son.addEventListener("click", function (e) {
console.log("son 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);
window.addEventListener("click", function (e) {
// e.target.nodeName 指当前点击的元素, e.currentTarget.nodeName绑定监听事件的元素
console.log("window 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);
parent.addEventListener("click", function (e) {
console.log("parent 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);
child.addEventListener("click", function (e) {
console.log("child 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);
son.addEventListener("click", function (e) {
console.log("son 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);
复制代码
点击红色方块
阻止事件传播
- e.stopPropagation()
大家经常听到的可能是阻止冒泡,实际上这个方法不只能阻止冒泡,还能阻止捕获阶段的传播。
- stopImmediatePropagation()
如果有多个相同类型事件的事件监听函数绑定到同一个元素,当该类型的事件触发时,它们会按照被添加的顺序执行。如果其中某个监听函数执行了 event.stopImmediatePropagation() 方法,则当前元素剩下的监听函数将不会被执行。
看一下代码
阻止默认行为
e.preventDefault()
e.preventDefault()可以阻止事件的默认行为发生,默认行为是指:点击a标签就转跳到其他页面、拖拽一个图片到浏览器会自动打开、点击表单的提交按钮会提交表单等等,因为有的时候我们并不希望发生这些事情,所以需要阻止默认行为
看一下代码
兼容性
attachEvent——兼容:IE7、IE8; 不支持第三个参数来控制在哪个阶段发生,默认是绑定在冒泡阶段
addEventListener——兼容:firefox、chrome、IE、safari、opera;
绑定事件的运用,以及封装一个多浏览器兼容的绑定事件函数
大家常见的一个面试题可能是ul + li,点击每个li alert对应的索引,这里就给大家来写一下看看
- 先来给每个li绑定事件
- 再来写一个事件委托的方式
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
</ul>
</body>
<script type="text/javascript">
const ul = document.querySelector("ul");
ul.addEventListener('click', function (e) {
const target = e.target;
if (target.tagName.toLowerCase() === "li") {
const liList = this.querySelectorAll("li");
index = Array.prototype.indexOf.call(liList, target);
alert(`内容为${target.innerHTML}, 索引为${index}`);
}
})
// const liList = document.getElementsByTagName("li");
// for(let i = 0; i<liList.length; i++){
// liList[i].addEventListener('click', function(e){
// alert(`内容为${e.target.innerHTML}, 索引为${i}`);
// })
// }
</script>
<style>
#ul {
background-color: gray;
width: 700px;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
}
li {
margin-bottom: 20px;
width: 80%;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
background-color: lightgoldenrodyellow;
font-size: 20px;
font-weight: bold;
}
</style>
</html>
复制代码
封装一个多浏览器兼容的绑定事件函数
class BomEvent {
constructor(element) {
this.element = element;
}
addEvent(type, handler) {
if (this.element.addEventListener) {
//事件类型、需要执行的函数、是否捕捉
this.element.addEventListener(type, handler, false);
} else if (this.element.attachEvent) {
this.element.attachEvent('on' + type, function () {
handler.call(element);
});
} else {
this.element['on' + type] = handler;
}
}
removeEvent(type, handler) {
if (this.element.removeEnentListener) {
this.element.removeEnentListener(type, handler, false);
} else if (element.datachEvent) {
this.element.detachEvent('on' + type, handler);
} else {
this.element['on' + type] = null;
}
}
}
// 阻止事件 (主要是事件冒泡,因为IE不支持事件捕获)
function stopPropagation(ev) {
if (ev.stopPropagation) {
ev.stopPropagation(); // 标准w3c
} else {
ev.cancelBubble = true; // IE
}
}
// 取消事件的默认行为
function preventDefault(event) {
if (event.preventDefault) {
event.preventDefault(); // 标准w3c
} else {
event.returnValue = false; // IE
}
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END