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 事件委託這個知識點啦~
【歡迎指正,碼字不易,喜歡請點贊哦】