先看幾道面試題 1. 描述下js裡面的事件流 2. 預設情況下,事件是在冒泡階段執行還是捕獲階段執行 3. 請簡要說明事件委托原理和使用場景 4. 手寫原生js實現事件代理,註意瀏覽器相容 如果上面的面試題,您不能很順利的作答,那麼希望這篇文件對您能有一些幫助。如果出現錯誤,請您及時指正,謝謝。 什 ...
先看幾道面試題
- 描述下js裡面的事件流
- 預設情況下,事件是在冒泡階段執行還是捕獲階段執行
- 請簡要說明事件委托原理和使用場景
- 手寫原生js實現事件代理,註意瀏覽器相容
如果上面的面試題,您不能很順利的作答,那麼希望這篇文件對您能有一些幫助。如果出現錯誤,請您及時指正,謝謝。
什麼是事件委托
事件委托也叫事件代理,《Javascript高級程式設計》中寫道:事件委托就是利用事件冒泡,只指定一個事件處理程式,就可以管理某一類型的所有事件。
想要理解事件委托,需要先理解js事件流。
js事件流
圖為事件流的全過程,從圖中我們可以看出:
- 一個完整的事件流是從window開始,最後回到window的一個過程
- 事件流被分為3個階段:1-5捕獲階段,5-6:目標階段,6-10:冒泡階段
觀察代碼,並說出列印結果
html
<body>
<div style="height: 100px;background: grey;"></div>
</body>
js
var oDiv = document.querySelector('#div');
oDiv.addEventListener('click',function(){
console.log('div')
});
document.body.addEventListener('click',function(){
console.log('body')
});
var oDiv = document.querySelector('#div');
oDiv.addEventListener('click',function(){
console.log('div')
},false);
document.body.addEventListener('click',function(){
console.log('body')
},false);
var oDiv = document.querySelector('#div');
oDiv.addEventListener('click',function(){
console.log('div')
},true);
document.body.addEventListener('click',function(){
console.log('body')
},true);
通過上述代碼,我們發現:
- addEventListener有3個參數:event, function, useCapture(可選)
- useCapture為true時,事件在捕獲階段執行,列印結果為body、div
- useCapture為false時,事件在冒泡階段執行,列印結果為div、body
- useCapture值不傳時,事件在冒泡階段執行,列印結果為div、body
阻止預設冒泡行為,我們可以用
ev.stopPropagation()
現在,我們認識了js事件流,接下來,說一說事件委托。
js事件委托
我們先來看一個場景:
現在有一個todo list,需要實現以下功能:
- 點擊‘添加’按鈕時,列表中新增item,內容為文本框輸入內容
- todo Item 點擊'完成'按鈕時自動刪除
我們知道的給DOM綁定事件的方法有以下幾種:
嵌入DOM
例如:
<li>想你 <button onclick="complete">完成</button></li>
直接綁定
oBtn.onclick = complete
事件監聽
oBtn.addEventListener('click',complete)
需求很簡單,但是有兩點需要註意:
- 如果給‘完成’按鈕綁定事件,因為不斷有新DOM的生成,每次生成都要重新綁定,代碼不便管理
- 如果給‘完成’按鈕綁定事件,則需要迴圈遍歷每一個‘完成’按鈕,不斷訪問DOM,按鈕越多,訪問次數越多,性能越低
為瞭解決上述兩個問題,‘事件代理’便是完美的解決方案。
實現思路
將事件綁定到父元素ul上,當用戶點擊按鈕時,通過事件流,冒泡到父元素ul,從而執行回調。
事件代理的好處
- 只綁定一次事件,無頻繁訪問DOM,性能較高
- 當有新DOM生成時,無需重覆綁定事件,代碼清晰簡潔
現在,我們使用事件代理的方式實現上述需求,可是我應該如何判斷點擊的是‘完成’按鈕,而不是其他元素呢?
事件對象
事件對象是在事件發生時產生,用來記錄事件發生時的一些信息。
oUl.addEventListener('click',function(ev){
var ev = ev || event;
console.log(ev);
})
事件對象中記錄了點擊事件發生時的目標元素,但由於瀏覽器存在差異,所以相容性的寫法為:
var origin = ev.target || ev.srcElement
手寫原生js實現事件代理,瀏覽器相容
function bindEvent(obj, type, fn) {
if (obj.addEventListener) {
obj.addEventListener(type, eventFn);
} else {
obj.attachEvent("on" + type, eventFn);
}
function eventFn(ev) {
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
fn && fn(target, ev)
}
}