JS事件的捕獲和冒泡階段
這里介紹兩個事件模型:IE事件模型與DOM事件模型
- IE內核瀏覽器的事件模型是冒泡型事件(沒有捕獲事件過程),事件句柄的觸發順序是從ChildNode到ParentNode。
<div id="ancestor"> <button id="child"> child</button> <button id="child2"> child2</button>
</div>
以上的HTML代碼在IE內核下,事件是這樣傳播的:
1、Button#child;
2、div#ancestor;
3、Body;
4、DocumentDOM標準的瀏覽器事件是:捕獲事件和冒泡事件。
捕獲事件過程:
1、Window
2、Document
3、Body
4、Div#ancestor
5、Button#child冒泡事件過程:
6、Div#ancestor
7、Body
8、Document
9、Window
當開發者在一個元素上注冊了事件后,這個事件的響應順序是從window(最頂層)開始一級一級的向下傳播,然后到了該元素后事件捕獲過程結束,事件開如冒泡,一級一級向父層元素冒泡(請注意第6步)。
當然,開發者可以很輕松的決定DOM標準的瀏覽器中的事件需要在哪個傳播過程觸發。
事件的注冊機制
DOM標準的瀏覽器事件注冊方法是通過addEventListener方法注冊,而IE內核的瀏覽器則是通過attachEvent方法注冊。
addEventListener
addEventListener方法帶有三個參數,分別是:eventType、handler、useCapture。
a. eventType不帶有on字符串;
b. handler參數是一個事件句柄,這個函數或方法帶有一個事件對象參數;
c. useCapture參數決定了事件句柄觸發在哪種事件傳播階段,如果useCapture為true則為捕獲階段,反之則為冒泡階段。
// html文件<div id="ancestor"> <button id="child"> child</button> <button id="child2"> child2</button>
</div>
// js代碼
let ancestorHandler = function (e){ console.log("div");
}
let childHandler = function (e){ console.log("button");
};document.querySelector('#ancestor').addEventListener('click',ancestorHandler,false);//注意第三個參數 ,注冊了一個在冒泡階段觸發的事件句柄document.querySelector('#child').addEventListener('click',childHandler,true);//注意第三個參數 ,注冊了一個在捕獲階段的事件句柄
輸出結果:
"button"
"div"
當用戶在這個DIV元素上點擊時,事件的執行順序是childHandler、ancestorHandler。
原因:按鈕的事件是在捕獲階段觸發的,也就是從上到下,而DIV的事件是注冊在冒泡階段,也就是點擊了這個按鈕開始從這個按鈕的位置往上冒泡。
其中useCapture我們設置了true,我們選擇在捕獲階段處理該handler。
其中useCapture我們設置了false,我們選擇在冒泡階段處理該handler。
我們來看一下事件的流程:
- 當用戶點擊DIV上的元素時,觸發監聽事件
- 執行childHandler
- 執行ancestorHandler
原因:按鈕的事件是在捕獲階段觸發的,也就是從上到下的順序,而DIV的事件是注冊在冒泡階段,也就是在點擊了這個按鈕button之后,從這個位置往上冒泡。
阻止事件的冒泡:
DOM事件對象提供了stopPropagation方法用于阻止事件流。
var ancestorHandler = function (e){ //......
},
childHandler = function (e){ e.stopPropagation(); //......
};
以上代碼在childHandler函數中添加了e.stopPropagation()代碼片段,它將阻止事件流,事件流包括捕獲階段及冒泡階段的事件流。
再修改上面的代碼如下:
let ancestorHandler = function (e){ console.log("div");
}
let childHandler = function (e){ console.log("button");
};
document.querySelector('#ancestor').addEventListener('click',ancestorHandler,true);//注意第三個參數
document.querySelector('#child').addEventListener('click',childHandler,true);//注意第三個參數
輸出結果:
// 點擊child
"div"
"button"
// 點擊child2
"div"
分析一下上面的代碼:
點擊child按鈕
1.當用戶點擊div內的child按鈕元素時,將會依次觸發ancestorHandler、childHandler函數
2.原因是我們將div#ancestor的事件注冊到捕獲階段了,也就是從上到下。
3.用戶點擊div區域,僅僅先觸發了ancestorHandler函數,因為事件流被阻止了
4.處理完ancestorHandler事件之后,再捕獲到child點擊事件,然后執行childHandler函數
點擊child2按鈕
1.當用戶點擊child按鈕時,會觸發點擊div內的元素觸發后的handler,也就是ancestorHandler函數
2.然后輸出div
removeEventListener
刪除監聽事件處理函數的方法,主要是針對移除 addEventListener() 方法添加的監聽事件。
removeEventListener同樣帶有三個參數,分別是:eventType、handler、useCapture,和addEventListener是同樣的。
// 向 <div> 元素添加事件句柄
document.getElementById("myDIV").addEventListener("mousemove", myFunction, false);// 移除 <div> 元素的事件句柄
document.getElementById("myDIV").removeEventListener("mousemove", myFunction, false);
示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vincent</title>
<style>
#myDIV
{background-color: coral;border: 1px solid;padding: 50px;color: white;
}
</style>
</head>
<body><div id="myDIV"> div 元素添加了 onmousemove 事件句柄,在你移動鼠標時會顯示隨機數。<p>點擊按鈕移除 DIV 的事件句柄。</p><button onclick="removeHandler()" id="myBtn">點我</button>
</div>
<p id="demo"></p>
<script>
document.getElementById("myDIV").addEventListener("mousemove", myFunction);
function myFunction()
{document.getElementById("demo").innerHTML = Math.random();
}
function removeHandler()
{document.getElementById("myDIV").removeEventListener("mousemove", myFunction);
}
</script></body>
</html>