JavaScript 事件流模型及事件委托详解

JavaScript 中的事件流模型 事件冒泡事件捕获,以及 事件委托(也叫事件代理),是前端面试中经常出现的知识点,作为一名前端工程师,梳理基础知识点对你一定有所帮助。

文章中所有的代码都有 codepen 实例,链接在对应的章节内,请同学们尝试自己修改运行代码,以便加深理解。

一、名词解释

在开始讲解之前,我们先熟悉几个概念

事件

事件是可以被 JavaScript 侦测到的行为。
onclick onload onchange 等事件。

事件流

事件在页面中的响应顺序

事件流模型

为了更好的理解事件流模型,我们把 DOM 树想象成一个靶子,父节点在外,子节点在内。如下图所示:

JavaScript 事件流模型及事件委托详解_第1张图片

  • 事件冒泡(event bubbling) 由内向外,即从 DOM 树的子到父,div -> body -> html -> document
  • 事件捕获(event capturing) 由外向内,即从 DOM 树的父到子,document -> html -> body -> div

接下来我们先通过代码实例详细讲解事件冒泡和事件捕获,然后讲解事件委托,并实现一个事件委托的实例。

二、事件冒泡 vs. 事件捕获

代码地址: https://codepen.io/cecillia/p...

事件冒泡事件捕获 分别由 微软网景 公司提出,后来 W3C 将两者结合,平息了战火,制定了统一的标准 —— 先捕获再冒泡

addEventListener

在 JavaScript 中,addEventListener 方法用于向指定元素添加事件句柄。
语法:element.addEventListener(event, function, useCapture)

element 目标元素
event 事件名,如 click
function 事件触发时执行的函数
useCapture Bool值,true - 事件句柄在 捕获 阶段执行,false- false- 默认。事件句柄在 冒泡 阶段执行

事件冒泡

来看一段代码实例,思考运行后会弹出什么。

/**.html**/
document
html
body
div
/**.js**/ var $t0 = document.getElementsByClassName('t0')[0]; var $t1 = document.getElementsByClassName('t1')[0]; var $t2 = document.getElementsByClassName('t2')[0]; var $t3 = document.getElementsByClassName('t3')[0]; $t0.addEventListener("click", function(){ alert("click div") }, false); $t1.addEventListener("click", function(){ alert("click body") }, false); $t2.addEventListener("click", function(){ alert("click html") }, false); $t3.addEventListener("click", function(){ alert("click document") }, false);

根据冒泡事件流模型由内向外的规则,会依次弹出:click div -> click body -> click html -> click docuement

事件捕获

将上一段代码中的 false 都改为 ture,则变为捕获方式:

/**.html**/
document
html
body
div
/**.js**/ var $t0 = document.getElementsByClassName('t0')[0]; var $t1 = document.getElementsByClassName('t1')[0]; var $t2 = document.getElementsByClassName('t2')[0]; var $t3 = document.getElementsByClassName('t3')[0]; $t0.addEventListener("click", function(){ alert("click div") }, true); $t1.addEventListener("click", function(){ alert("click body") }, true); $t2.addEventListener("click", function(){ alert("click html") }, true); $t3.addEventListener("click", function(){ alert("click document") }, true);

根据捕获事件流模型由外向内的规则,会依次弹出:click document -> click html -> click body -> click div

事件冒泡&事件捕获同时存在

如果两种事件流模型同时存在会怎样展示呢?

/**.html**/
document
html
body
div
/**.js**/ var $t0 = document.getElementsByClassName('t0')[0]; var $t1 = document.getElementsByClassName('t1')[0]; var $t2 = document.getElementsByClassName('t2')[0]; var $t3 = document.getElementsByClassName('t3')[0]; $t0.addEventListener("click", function(){ alert("click div") }, false); $t1.addEventListener("click", function(){ alert("click body") }, false); $t2.addEventListener("click", function(){ alert("click html") }, true); $t3.addEventListener("click", function(){ alert("click document") }, true);

原则:

  • 从外向内,捕获前进,遇到捕获事件立即执行
  • 非 target 节点,先捕获再冒泡
  • target 节点,按代码书写顺序执行(无论冒泡还是捕获)

因此会依次弹出:click document -> click html -> click div -> click body

三、事件流模型的应用:事件委托

代码地址: https://codepen.io/cecillia/p...

事件委托 又叫 事件代理,指的是利用事件冒泡原理,只需给外层父容器添加事件,若内层子元素有点击事件,则会冒泡到父容器上,这就是事件委托,简单说就是:子元素委托它们的父级代为执行事件。

事件流模型在业务开发中有哪些应用场景呢?

例如,一个播放列表有成千上万首歌曲,如果遍历播放列表,给每个 item 添加点击事件,而这样无疑会多次访问 DOM,每次 DOM 操作都会引起浏览器的重绘与重排,非常不利于性能优化。

这时候我们可以利用事件委托,只给最外层的容器添加响应事件,这样只需一次 DOM 操作,就能达到目的。

/**.html**/
  • 青花瓷
  • 东风破
  • 双节棍
/**.js**/ var $music = document.getElementById('music'); $music.addEventListener('click', function(e) { if(e.target.nodeName.toLowerCase() === 'li') { // 判断目标元素target是否为li元素 var content = e.target.innerHTML; console.log(content); } }, false)

怎么样,事件委托也不过如此吧?只要掌握了事件冒泡、事件捕获的原理,并将其运用到实际业务开发中,就能够真正 get 事件委托这个知识点啦~


【欢迎指正,码字不易,喜欢请点赞哦】

你可能感兴趣的:(javascript)