js的事件流你真的弄明白了吗?

Posted 韩金金金

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js的事件流你真的弄明白了吗?相关的知识,希望对你有一定的参考价值。

当浏览器发展到第四代时候,浏览器开发团队遇到了一个有意思的问题;页面的哪一部分会拥有某个特地的事件?要明白这个问题问的是什么,可以想象画在纸上的一组同心圆,如果你把手指放在圆心上,那么你的手指指向的不是一个圆,而是纸上所有的圆。两家公司的开发团队在这件事情上的看法是一致的,如果你单击了某个按钮,他们都认为单击事件不仅发生在按钮上,更是发生在了整个容器元素,甚至是整个页面。

那么对于页面接收事件的顺序两者出现了不同的定义:捕获流 ( chrome )冒泡流( IE )。在事件监听addEventListener的第三个参数中,捕获流是 true,冒泡流是 false。主流浏览器默认的都是冒泡流机制,也就是第三个参数默认false。

事件捕获:window -> document -> html -> body -> button  (原先的‘DOM2级事件’本来是规定事件应该从document对象出发的,但是后面几乎所有的浏览器还是拓展到了window对象层面)

事件冒泡:button -> body -> html -> document -> window  

事实上‘DOM2级事件’规定的事件流包括了三个阶段: 事件捕获阶段,处于目标阶段,事件冒泡阶段。(原先是规定捕获阶段不涉及事件目标的,但是后面高版本都会在捕获阶段触发事件对象上的事件)

重点:处于目标阶段,即button的事件在捕获阶段和冒泡阶段都会被触发。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
.box
{
    position: relative;
    background-color: coral;
    border: 1px solid;
    padding: 50px;
}
.child {
    position: relative;
    background-color: pink;
    width: 100px;
    height: 100px;
}
</style>
</head>
<body>
<div id="A" class="box">
    <div id="B">
        <div id="C" class="child">点击该方块, 我是冒泡</div>
    </div>
</div><br>
<script>
document.getElementById("A").addEventListener("click", function(e)
{
    console.log("1 捕获");
}, true);
document.getElementById("B").addEventListener("click", function(e)
{
    console.log("2 捕获");
    // e.stopPropagation()
}, true);
document.getElementById("C").addEventListener("click", function(e)
{
    console.log("捕获阶段触发目标");
    // e.stopPropagation()
}, true);
document.getElementById("C").addEventListener("click", function()
{
    console.log("冒泡触发目标");
}, false);
document.getElementById("B").addEventListener("click", function()
{
    console.log("1 冒泡");
}, false);
document.getElementById("A").addEventListener("click", function()
{
    console.log("2 冒泡");
}, false);

</script>

</body>
</html>

  

 

执行结果如下:

 

值得注意的是,捕获阶段或者冒泡阶段对应的在 C 上的事件其实并不是 先捕获阶段触发目标 然后是冒泡阶段触发目标,在C上的事件其实是都是触发的,触发顺序取决于你写的顺序。不信,换下顺序如下

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
.box
{
    position: relative;
    background-color: coral;
    border: 1px solid;
    padding: 50px;
}
.child {
    position: relative;
    background-color: pink;
    width: 100px;
    height: 100px;
}
</style>
</head>
<body>
<div id="A" class="box">
    <div id="B">
        <div id="C" class="child">点击该方块, 我是冒泡</div>
    </div>
</div><br>
<script>
document.getElementById("A").addEventListener("click", function(e)
{
    console.log("1 捕获");
}, true);
document.getElementById("B").addEventListener("click", function(e)
{
    console.log("2 捕获");
    // e.stopPropagation()
}, true);
document.getElementById("C").addEventListener("click", function()
{
    console.log("冒泡触发目标");
}, false);
document.getElementById("C").addEventListener("click", function(e)
{
    console.log("捕获阶段触发目标");
    // e.stopPropagation()
}, true);

document.getElementById("B").addEventListener("click", function()
{
    console.log("1 冒泡");
}, false);
document.getElementById("A").addEventListener("click", function()
{
    console.log("2 冒泡");
}, false);

</script>

</body>
</html>

 执行结果如下:

 

接下来继续说一下阻止冒泡,假设我在  console.log("1 捕获") 或者 console.log("2 捕获") 后面加上  e.stopPropagation(),后面的事件还能触发吗?单单是只把冒泡流扼杀了吗?

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
.box
{
    position: relative;
    background-color: coral;
    border: 1px solid;
    padding: 50px;
}
.child {
    position: relative;
    background-color: pink;
    width: 100px;
    height: 100px;
}
</style>
</head>
<body>
<div id="A" class="box">
    <div id="B">
        <div id="C" class="child">点击该方块, 我是冒泡</div>
    </div>
</div><br>
<script>
document.getElementById("A").addEventListener("click", function(e)
{
    console.log("1 捕获");
    e.stopPropagation()
}, true);
document.getElementById("B").addEventListener("click", function(e)
{
    console.log("2 捕获");
    
}, true);
document.getElementById("C").addEventListener("click", function()
{
    console.log("冒泡触发目标");
}, false);
document.getElementById("C").addEventListener("click", function(e)
{
    console.log("捕获阶段触发目标");
    // e.stopPropagation()
}, true);

document.getElementById("B").addEventListener("click", function()
{
    console.log("1 冒泡");
}, false);
document.getElementById("A").addEventListener("click", function()
{
    console.log("2 冒泡");
}, false);

</script>

</body>
</html>

  

事实上,它是把后面的事件流都打断了。而如果 e.stopPropagation() 是写在  console.log("捕获阶段触发目标") 或者  console.log("冒泡触发目标")后面,目标阶段都点击事件都会执行,执行按你写的顺序来。

Propagation英文意思是‘传播’ stopPropagation 就是阻止事件传播,阻止事件流的继续往下发生。

所以stopPropagation()方法事实上是打断了事件流继续执行,而我们一般时候是直接写在目标事件的点击函数里,就起到了阻止冒泡的作用!

  

以上是关于js的事件流你真的弄明白了吗?的主要内容,如果未能解决你的问题,请参考以下文章

选择排序,你真的明白了吗?

吊打面试官之一文吃透JS事件循环EventLoop

「高频面试题」女友:消息队列 和 事件循环系统终于弄明白了!(内附思维导图)

jscript定时器,一直用的东西,你真的明白吗?

用了这么久,你真的真的明白 HttpClient 的实现原理了吗?

ArrayList的删除姿势你都知道了吗