一、DOM 事件模型
DOM 事件模型包括捕獲和冒泡,捕獲是從上往下到達目標元素,冒泡是從當前元素,也就是目標元素往上到 window
二、流
流的概念,在現今的 JavaScript 中隨處可見。比如說 React 中的單向數據流,Node 中的流,還有 DOM 事件流,都是流的一種生動體現。
至於流的具體概念,用術語說流是對輸入輸出設備的抽象。以程序的角度說,流是具有方向的數據。
三、事件流
瀏覽器在為當前頁面與用戶做交互的過程中,比如點擊鼠標左鍵,會出現這個左鍵是怎么傳到頁面上,還有怎么響應的問題。
事件流所描述的就是從頁面中接受事件的順序,事件流分為兩種:事件冒泡(主流)和事件捕獲
1、事件冒泡
事件開始時由具體元素接收,然后逐級向上傳播到父元素
舉個例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Event Bubbling</title> </head> <body> <button id="clickMe">Click Me</button> </body> </html>
我們給 button 和它的父元素,加入點擊事件
var button = document.getElementById('clickMe'); button.onclick = function() { console.log('1. You click Button'); }; document.body.onclick = function() { console.log('2. You click body'); }; document.onclick = function() { console.log('3. You click document'); }; window.onclick = function() { console.log('4. You click window'); };
點擊按鈕運行效果:
也就是說,click 事件首先在 <button> 元素上發生,然后逐級向上傳播,這就是事件冒泡
2、事件捕獲
父元素的節點更早接收事件,而具體元素最后接收事件,與事件冒泡相反
三、DOM 事件流
DOM事件流包括三個階段:
-
事件捕獲階段
-
處於目標階段
-
事件冒泡階段
1、事件捕獲階段
當事件發生時,首先發生的是事件捕獲,為父元素截獲事件提供了機會
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Event Bubbling</title> </head> <body> <button id="clickMe">Click Me</button> </body> </html>
上面事件冒泡的 Demo 中,window 點擊事件更改為使用事件捕獲模式
var button = document.getElementById('clickMe'); button.onclick = function() { console.log('1. You click Button'); }; document.body.onclick = function() { console.log('2. You click body'); }; document.onclick = function() { console.log('3. You click document'); }; // window.onclick = function() { // console.log('4. You click window'); // }; window.addEventListener('click', function() { console.log('4. You click window'); }, true);
此時,點擊 button 的效果是這樣的:
可以看到,點擊事件先被父元素截獲了,且該函數只在事件捕獲階段起作用
2、處於目標階段
事件到了具體元素時,在具體元素上發生,並且被看成冒泡階段的一部分
3、事件冒泡階段
最后,冒泡階段發生,事件開始冒泡
四、阻止事件冒泡
事件冒泡過程,是可以被阻止的。防止事件冒泡而帶來不必要的錯誤和困擾。
阻止方法是使用 stopPropagation(),舉個例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Event Bubbling</title> </head> <body> <button id="clickMe">Click Me</button> </body> </html>
還是上面的 demo,這里對 button 的 click 事件做了一些改造:
var button = document.getElementById('clickMe'); // button.onclick = function() { // console.log('1. You click Button'); // }; button.addEventListener('click', function(event) { // 這里event為事件對象 console.log('1. You click Button'); event.stopPropagation(); console.log('Stop Propagation!'); }, false); document.body.onclick = function() { console.log('2. You click body'); }; document.onclick = function() { console.log('3. You click document'); }; window.addEventListener('click', function() { console.log('4. You click window'); }, true);
點擊后,效果如下圖:
不難看出,事件在到達具體元素后,停止了冒泡,但不影響父元素的事件捕獲
五、DOM0級事件
DOM0級事件,就是直接通過 onclick 等方式實現相應的事件
1、標簽內寫 onclick 事件
<input id="myButton" type="button" value="Click Me" onclick="alert('Hello1');" >
2、在 JS 中 使用onclick = function(){}
document.getElementById("myButton").onclick = function () { alert('Hello2'); }
運行結果 - 點擊彈出:
這說明 DOM0 級添加事件時,后面的事件會覆蓋前面的事件,而 DOM2級則不會,多個事件都會執行;
另外,DOM0級事件具有很好的跨瀏覽器優勢,會以最快的速度綁定,但由於綁定速度太快,可能頁面還未完全加載出來,以至於事件可能無法正常運行
六、DOM2級事件
1、DOM2級事件的方法
主流瀏覽器 DOM2 級事件是通過以下兩個方法用於處理指定和刪除事件處理程序的操作:
- addEvenetListener
- removeEventListener
2、DOM2級事件的使用
所有的 DOM 節點都包含這兩個方法,使用方法如下:
- target.addEventListener(type, listener[, useCapture]);
- target.removeEventListener(type, listener[, useCapture]);
並且它們都接受三個參數:
- type:事件類型,如'click'、'mouseover'、'mouseout',在事件名前不加'on'
- listener:事件處理方法
- useCapture:布爾參數,不傳該參數時默認是 false,表示在事件冒泡階段處理,如果是 true,則表示在捕獲階段調用事件處理程序
舉個例子:
<input id="myButton" type="button" value="Click Me" onclick="alert('Hello1');" >
document.getElementById("myButton").onclick = function () { alert('Hello2'); } document.getElementById('myButton').addEventListener('click', function() { alert('Hello3'); }, true) document.getElementById('myButton').addEventListener('click', function() { alert('Hello4'); }, true) document.getElementById('myButton').addEventListener('click', function() { alert('Hello5'); }, false)
運行結果:
注意:只有 DOM2級事件包含以下三個階段
-
事件捕獲階段
-
處於目標階段
-
事件冒泡階段