DOM 事件机制-冒泡与捕获

138 阅读3分钟

DOM事件机制,是指dom如何响应事件的
有一个这样的html页面

<div id='div1' class="circle red">
    <div id='div2' class="circle orange">
        <div id='div3' class="circle yellow">
            <div id='div4' class="circle green">
                <div id='div5' class="circle cyan">
                    <div id='div6' class="circle blue">
                        <div id='div7' class="circle purple">
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

image.png
添加点击事件

div1.addEventListener("click", (e) => { console.log("div1 响应click事件")});
div2.addEventListener("click", (e) => { console.log("div2 响应click事件")});
div3.addEventListener("click", (e) => { console.log("div3 响应click事件")});
div4.addEventListener("click", (e) => { console.log("div4 响应click事件")});
div5.addEventListener("click", (e) => { console.log("div5 响应click事件")});
div6.addEventListener("click", (e) => { console.log("div6 响应click事件")});
div7.addEventListener("click", (e) => { console.log("div7 响应click事件")});

鼠标点击最中间,点击的范围落在了div1~div7上,那么,点击事件的响应式个什么情况呢? 响应情况如下:

-   "div7 响应click事件"
-   "div6 响应click事件"
-   "div5 响应click事件"
-   "div4 响应click事件"
-   "div3 响应click事件"
-   "div2 响应click事件"
-   "div1 响应click事件"

这种响应模式,是先从最里面的儿子节点响应,儿子节点响应完了,再把事件传递给父亲节点,最后传递给最上层的节点,事件从小范围传递给大范围,有点像水泡从里面冒出来,叫做:
冒泡模式,事件从里向外,从小向大,从子向父传递
添加事件响应方法时,如果不加额外的参数,js会默认在冒泡时调用;
实际上,还有一种响应模式,是从最外面的父亲节点响应,父亲节点响应完了,再把事件分发给儿子节点处理,最后传递给最里面的节点,事件从大范围传递给小范围,有点像父亲捕获到了事件,再分发给孩子处理,这种事件的传递模式,叫做:
捕获模式,事件从外向里,从大向小,从父向子传递
让事件响应方法按照捕获模式响应,需要在addEventListener方法上传递方式参数,true:

div1.addEventListener("click", (e) => { console.log("div1 响应click事件")}, true);
div2.addEventListener("click", (e) => { console.log("div2 响应click事件")}, true);
div3.addEventListener("click", (e) => { console.log("div3 响应click事件")}, true);
div4.addEventListener("click", (e) => { console.log("div4 响应click事件")}, true);
div5.addEventListener("click", (e) => { console.log("div5 响应click事件")}, true);
div6.addEventListener("click", (e) => { console.log("div6 响应click事件")}, true);
div7.addEventListener("click", (e) => { console.log("div7 响应click事件")}, true);

这是,点击事件的响应情况如下:

-   "div1 响应click事件"
-   "div2 响应click事件"
-   "div3 响应click事件"
-   "div4 响应click事件"
-   "div5 响应click事件"
-   "div6 响应click事件"
-   "div7 响应click事件"

如果节点同时添加了两种模式下的处理方法呢?

div1.addEventListener("click", (e) => { console.log("div1 响应click事件")});
div2.addEventListener("click", (e) => { console.log("div2 响应click事件")});
div3.addEventListener("click", (e) => { console.log("div3 响应click事件")});
div4.addEventListener("click", (e) => { console.log("div4 响应click事件")});
div5.addEventListener("click", (e) => { console.log("div5 响应click事件")});
div6.addEventListener("click", (e) => { console.log("div6 响应click事件")});
div7.addEventListener("click", (e) => { console.log("div7 响应click事件")});
div1.addEventListener("click", (e) => { console.log("div1 响应click事件")}, true);
div2.addEventListener("click", (e) => { console.log("div2 响应click事件")}, true);
div3.addEventListener("click", (e) => { console.log("div3 响应click事件")}, true);
div4.addEventListener("click", (e) => { console.log("div4 响应click事件")}, true);
div5.addEventListener("click", (e) => { console.log("div5 响应click事件")}, true);
div6.addEventListener("click", (e) => { console.log("div6 响应click事件")}, true);
div7.addEventListener("click", (e) => { console.log("div7 响应click事件")}, true);

这种情况下事件如何传递?
W3C规定浏览器应该同时支持两种调用顺序,首先按爷爷=>爸爸=>儿子顺序看有没有函数监听,然后按儿子=>爸爸=>爷爷顺序看有没有函数监听,有监听函数就调用,并提供事件信息,没有就跳过。运行结果如下所示:

-   "div1 响应click事件"
-   "div2 响应click事件"
-   "div3 响应click事件"
-   "div4 响应click事件"
-   "div5 响应click事件"
-   "div6 响应click事件"
-   "div7 响应click事件"
-   "div7 响应click事件"
-   "div6 响应click事件"
-   "div5 响应click事件"
-   "div4 响应click事件"
-   "div3 响应click事件"
-   "div2 响应click事件"
-   "div1 响应click事件"

可见,当前浏览器对于事件传递的处理是:
先捕获,后冒泡
上面所说的事件是最常见的点击事件,click,实际上,还有很多其他事件,比如常见的有,双击事件,dblclick,焦点获取事件,focus,等。
事件对象有许多属性,比如

  • target , 触发事件的节点
  • currentTarget ,监听事件的节点 关于事件,更多信息,可以参考: MDN事件
    附录,圆环实例理解冒泡与捕捉: js.jirengu.com/tulin/3/edi…