JavaScript 中的事件流模型 事件冒泡
和 事件捕获
,以及 事件委托
(也叫事件代理),是前端面试中常常出现的知识点,做为一名前端工程师,梳理基础知识点对你必定有所帮助。html
文章中全部的代码都有 codepen 实例,连接在对应的章节内,请同窗们尝试本身修改运行代码,以便加深理解。前端
在开始讲解以前,咱们先熟悉几个概念node
事件是能够被 JavaScript 侦测到的行为。
如 onclick
onload
onchange
等事件。面试
事件在页面中的响应顺序浏览器
为了更好的理解事件流模型,咱们把 DOM 树想象成一个靶子,父节点在外,子节点在内。以下图所示:性能优化
事件冒泡(event bubbling)
由内向外,即从 DOM 树的子到父,div -> body -> html -> document
事件捕获(event capturing)
由外向内,即从 DOM 树的父到子,document -> html -> body -> div
接下来咱们先经过代码实例详细讲解事件冒泡和事件捕获,而后讲解事件委托,并实现一个事件委托的实例。前端工程师
代码地址: https://codepen.io/cecillia/p...
事件冒泡
和 事件捕获
分别由 微软
和 网景
公司提出,后来 W3C
将二者结合,平息了战火,制定了统一的标准 —— 先捕获再冒泡。函数
在 JavaScript 中,addEventListener
方法用于向指定元素添加事件句柄。
语法:element.addEventListener(event, function, useCapture)
性能
element | 目标元素 | |
event | 事件名,如 click | |
function | 事件触发时执行的函数 | |
useCapture | Bool值,true - 事件句柄在 捕获 阶段执行,false- false- 默认。事件句柄在 冒泡 阶段执行 |
来看一段代码实例,思考运行后会弹出什么。优化
/**.html**/ <div class="t3">document <div class="t2">html <div class="t1">body <div class="t0">div</div> </div> </div> </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**/ <div class="t3">document <div class="t2">html <div class="t1">body <div class="t0">div</div> </div> </div> </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**/ <div class="t3">document <div class="t2">html <div class="t1">body <div class="t0">div</div> </div> </div> </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);
原则:
所以会依次弹出:click document -> click html -> click div -> click body
代码地址: https://codepen.io/cecillia/p...
事件委托
又叫 事件代理
,指的是利用事件冒泡原理,只需给外层父容器添加事件,若内层子元素有点击事件,则会冒泡到父容器上,这就是事件委托,简单说就是:子元素委托它们的父级代为执行事件。
事件流模型在业务开发中有哪些应用场景呢?
例如,一个播放列表有成千上万首歌曲,若是遍历播放列表,给每一个 item 添加点击事件,而这样无疑会屡次访问 DOM,每次 DOM 操做都会引发浏览器的重绘与重排,很是不利于性能优化。
这时候咱们能够利用事件委托,只给最外层的容器添加响应事件,这样只需一次 DOM 操做,就能达到目的。
/**.html**/ <ul id="music"> <li>青花瓷</li> <li>东风破</li> <li>双节棍</li> </ul> /**.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 事件委托这个知识点啦~
【欢迎指正,码字不易,喜欢请点赞哦】