前短时间有个面试,被问到浏览器事件机制,然后才感受到了,自己对底层知识有多差劲。
事件流程
先向下捕获,再向上冒泡。
image.png
简单来说,当你鼠标在浏览器上点击了一下。
- 浏览器捕获到了click事件。
- 然后浏览器根据你点击的事件,从window开始向下,就会触发每个父祖element捕获模式的事件回调。
- 直到找到点击所在的最终(最小的element)
- 然后浏览器开始继续又向上冒泡其父祖element的click事件,直至window。
- 默认的事件都是冒泡模式下触发的。
测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>event test</title>
</head>
<body>
<div >
<p>
<span>test</span>
</p>
</div>
<script>
window.addEventListener("click",clickCallback,true)
document.body.addEventListener("click",clickCallback,true)
document.querySelector("div").addEventListener("click",clickCallback,true)
document.querySelector("p").addEventListener("click",clickCallback,true)
document.querySelector("span").addEventListener("click",clickCallback,true)
document.body.addEventListener("click",clickCallback1)
window.addEventListener("click",clickCallback1)
document.querySelector("div").addEventListener("click",clickCallback1)
document.querySelector("p").addEventListener("click",clickCallback1)
document.querySelector("span").addEventListener("click",clickCallback1)
function clickCallback(e){
var nodeName = e.currentTarget == window?"window":e.currentTarget.nodeName;
console.log("捕获:",nodeName)
}
function clickCallback1(e){
var nodeName = e.currentTarget == window?"window":e.currentTarget.nodeName;
console.log("冒泡:",nodeName)
}
</script>
</body>
</html>
image.png
接下来
我们看看addEventListener第三个参数使用option,都又哪些
可用的选项如下:
-
capture
:Boolean
,表示listener
会在该类型的事件捕获阶段传播到该EventTarget
时触发。 -
once
:Boolean
,表示listener 在添加之后最多只调用一次。如果是
true,
listener
会在其被调用之后自动移除。 -
passive
:Boolean
,设置为true时,表示listener
永远不会调用preventDefault()。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。
以上三个参数都比较好理解。
最后再了解下
- 使用addEventListener为同一element设置多个clcik事件,其事件执行顺序?
答
与js执行addEventListener的顺序一致,即事件注册的顺序一致。 - 使用addEventListener为同一element设置多个clcik事件,前面的事件执行stopPropagation,能否阻止后面事件?
答
不能,只能阻止该事件(捕获)向下(冒泡)向上传播。
3.e.preventDefault()只是阻止浏览器默认事件。
如下demo:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>event test</title>
</head>
<body>
<div>
<p>
<span>test</span>
</p>
</div>
<script>
window.addEventListener("click", clickCallback, true)
document.body.addEventListener("click", clickCallback, true)
document.body.addEventListener("click", function testEvent1(e) {
e.stopPropagation();
e.preventDefault();
console.log("test1")
}, true)
document.body.addEventListener("click", function testEvent2() {
console.log("test2")
}, true)
document.querySelector("div").addEventListener("click", clickCallback, true)
document.querySelector("p").addEventListener("click", clickCallback, true)
document.querySelector("span").addEventListener("click", clickCallback, true)
document.body.addEventListener("click", clickCallback1)
window.addEventListener("click", clickCallback1)
document.querySelector("div").addEventListener("click", clickCallback1)
document.querySelector("p").addEventListener("click", clickCallback1)
document.querySelector("span").addEventListener("click", clickCallback1)
function clickCallback(e) {
var nodeName = e.currentTarget == window ? "window" : e.currentTarget.nodeName;
console.log("捕获:", nodeName)
}
function clickCallback1(e) {
var nodeName = e.currentTarget == window ? "window" : e.currentTarget.nodeName;
console.log("冒泡:", nodeName)
}
</script>
</body>
</html>
image.png
在捕获期阻止事件下传递后,该事件将不会再触发冒泡事件了。
网友评论