继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

事件捕获与事件冒泡

为爱心太软
关注TA
已关注
手记 170
粉丝 1.4万
获赞 860

绑定事件的接口

为节点绑定事件的方式大致可以分为以下三种:

例子:

直接将事件写在 HTML 文档中

<button onclick='showMessage()'>OK</button>

function showMessage() {
    console.log('hello');
};
//输出:hello

以节点属性的方式绑定事件

<button id='btn'>OK</button>

var btn = document.getElementById('btn')
btn.onclick = function() {
    console.log(this.id);
};
//输出:btn

使用 DOM 标准中事件绑定的方法

<button id='btn'>OK</button>

var btn = document.getElementById('btn')
btn.addEventListener('click', function() {
    console.log(this.id);
});
//输出:btn

这一部分的详细介绍,可以参考以下手记:
JavaScript事件处理程序

事件捕获与事件冒泡

每个事件都有属性来标记该事件的目标节点,当事件到达目标节点的时候,事件处理程序就会被调用。那么,事件流在 DOM 节点中是如何流动,直至找到目标节点的呢?针对这个问题,早期的渲染引擎有着不同的解析方式,也因此诞生了事件捕获和事件冒泡两种完全相反的事件流机制。

事件捕获

事件捕获强调,事件流由最外层开始,直到目标节点。

例子:

//代码中省略了 html、head 和 body 元素
<div>
<p>hello</p>
</div>

var document = document,
    html = document.documentElement,
    body = document.body,
    div = body.firstElementChild,
    p = div.firstElementChild;

document.addEventListener("click", () => {
    console.log("#document")
}, true);
html.addEventListener("click", () => {
    console.log("HTML")
}, true);
body.addEventListener("click", () => {
    console.log("body")
}, true);
div.addEventListener("click", () => {
    console.log("div")
}, true);
p.addEventListener("click", () => {
    console.log("p")
}, true);

例子中,事件流到达的顺序为:
#document -> HTML -> body -> div -> p

如果从 DOM 树上去观察事件流,顺序是由上而下:

图片描述

之所以早期的渲染引擎,例如网景浏览器,选择支持事件捕获机制,主要是从计算机的工作顺序来看待事件流的。实际上,当我们使用鼠标点击页面上的按钮时,并不会真正地" 触碰 "到按钮,我们只是简单地按了一下鼠标的键,是操作系统计算出鼠标的位置信息,反馈给浏览器,然后浏览器根据坐标信息,逐步找到目标节点。这个过程,就是由外而内进行的。

事件冒泡

与事件捕获完全相反,事件冒泡强调,事件流由最内层开始,直到根节点。

例子:

//代码中省略了 html、head 和 body 元素
<div>
    <p>hello</p>
</div>

var document = document,
    html = document.documentElement,
    body = document.body,
    div = body.firstElementChild,
    p = div.firstElementChild;

document.addEventListener("click", () => {
    console.log("#document")
});
html.addEventListener("click", () => {
    console.log("HTML")
});
body.addEventListener("click", () => {
    console.log("body")
});
div.addEventListener("click", () => {
    console.log("div")
});
p.addEventListener("click", () => {
    console.log("p")
});

例子中,事件流到达的顺序为:
p -> div -> body -> HTML -> #document

如果从 DOM 树上去观察事件流,顺序是由下而上:

图片描述

事件冒泡最早由微软公司提出,并应用到 IE 浏览器中。这一事件机制是比较符合人类逻辑思维的,因为当你按下电脑的关机键时,你也同时按在了电脑上。

统一标准

最后,规范制定者采取了一种折中的方式,统一了标准,即先捕获,再冒泡。并在 DOM 底层接口上,加入了 EventTarget (1)类型,用以控制事件是捕获还是冒泡。实际上,大部分开发者不需要关心事件捕获,我们只需要使用接口中默认的冒泡模式即可。

(1) 我们可以通过谷歌浏览器看一下 DOM 事件的接口。在谷歌浏览器中打印 Node.__proto__,可以发现 Node 节点的原型是 EventTarget;接下来打印 EventTarget.prototype,可以看到 DOM 绑定事件的方法 addEventListener 等都是写在 EventTarget 中。

图片描述

事件委托

具体内容,可以参考下面的手记:
JavaScrip事件委托


如有错误,欢迎指正,本人不胜感激。

打开App,阅读手记
1人推荐
发表评论
随时随地看视频慕课网APP