js的事件流你真的弄明白了嗎?
當瀏覽器發展到第四代時候,瀏覽器開發團隊遇到了一個有意思的問題;頁面的哪一部分會擁有某個特地的事件?要明白這個問題問的是什麽,可以想象畫在紙上的一組同心圓,如果你把手指放在圓心上,那麽你的手指指向的不是一個圓,而是紙上所有的圓。兩家公司的開發團隊在這件事情上的看法是一致的,如果你單擊了某個按鈕,他們都認為單擊事件不僅發生在按鈕上,更是發生在了整個容器元素,甚至是整個頁面。
那麽對於頁面接收事件的順序兩者出現了不同的定義:捕獲流 ( chrome )冒泡流( IE )。在事件監聽addEventListener的第三個參數中,捕獲流是 true,冒泡流是 false。主流瀏覽器默認的都是冒泡流機制,也就是第三個參數默認false。
事件捕獲:window -> document -> html -> body -> button (原先的‘DOM2級事件’本來是規定事件應該從document對象出發的,但是後面幾乎所有的瀏覽器還是拓展到了window對象層面)
事件冒泡:button -> body -> html -> document -> window
事實上‘DOM2級事件’規定的事件流包括了三個階段: 事件捕獲階段,處於目標階段,事件冒泡階段。(原先是規定捕獲階段不涉及事件目標的,但是後面高版本都會在捕獲階段觸發事件對象上的事件)
重點:處於目標階段,即button的事件在捕獲階段和冒泡階段都會被觸發。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <style> .box { position: relative; background-color: coral; border: 1px solid; padding: 50px; } .child { position: relative; background-color: pink; width: 100px; height: 100px; } </style> </head> <body> <div id="A" class="box"> <div id="B"> <div id="C" class="child">點擊該方塊, 我是冒泡</div> </div> </div><br> <script> document.getElementById("A").addEventListener("click", function(e) { console.log("1 捕獲"); }, true); document.getElementById("B").addEventListener("click", function(e) { console.log("2 捕獲"); // e.stopPropagation() }, true); document.getElementById("C").addEventListener("click", function(e) { console.log("捕獲階段觸發目標"); // e.stopPropagation() }, true); document.getElementById("C").addEventListener("click", function() { console.log("冒泡觸發目標"); }, false); document.getElementById("B").addEventListener("click", function() { console.log("1 冒泡"); }, false); document.getElementById("A").addEventListener("click", function() { console.log("2 冒泡"); }, false); </script> </body> </html>
執行結果如下:
值得註意的是,捕獲階段或者冒泡階段對應的在 C 上的事件其實並不是 先捕獲階段觸發目標 然後是冒泡階段觸發目標,在C上的事件其實是都是觸發的,觸發順序取決於你寫的順序。不信,換下順序如下
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
.box
{
position: relative;
background-color: coral;
border: 1px solid;
padding: 50px;
}
.child {
position: relative;
background-color: pink;
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<div id="A" class="box">
<div id="B">
<div id="C" class="child">點擊該方塊, 我是冒泡</div>
</div>
</div><br>
<script>
document.getElementById("A").addEventListener("click", function(e)
{
console.log("1 捕獲");
}, true);
document.getElementById("B").addEventListener("click", function(e)
{
console.log("2 捕獲");
// e.stopPropagation()
}, true);
document.getElementById("C").addEventListener("click", function()
{
console.log("冒泡觸發目標");
}, false);
document.getElementById("C").addEventListener("click", function(e)
{
console.log("捕獲階段觸發目標");
// e.stopPropagation()
}, true);
document.getElementById("B").addEventListener("click", function()
{
console.log("1 冒泡");
}, false);
document.getElementById("A").addEventListener("click", function()
{
console.log("2 冒泡");
}, false);
</script>
</body>
</html>
執行結果如下:
接下來繼續說一下阻止冒泡,假設我在 console.log("1 捕獲") 或者 console.log("2 捕獲") 後面加上 e.stopPropagation(),後面的事件還能觸發嗎?單單是只把冒泡流扼殺了嗎?
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
.box
{
position: relative;
background-color: coral;
border: 1px solid;
padding: 50px;
}
.child {
position: relative;
background-color: pink;
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<div id="A" class="box">
<div id="B">
<div id="C" class="child">點擊該方塊, 我是冒泡</div>
</div>
</div><br>
<script>
document.getElementById("A").addEventListener("click", function(e)
{
console.log("1 捕獲");
e.stopPropagation()
}, true);
document.getElementById("B").addEventListener("click", function(e)
{
console.log("2 捕獲");
}, true);
document.getElementById("C").addEventListener("click", function()
{
console.log("冒泡觸發目標");
}, false);
document.getElementById("C").addEventListener("click", function(e)
{
console.log("捕獲階段觸發目標");
// e.stopPropagation()
}, true);
document.getElementById("B").addEventListener("click", function()
{
console.log("1 冒泡");
}, false);
document.getElementById("A").addEventListener("click", function()
{
console.log("2 冒泡");
}, false);
</script>
</body>
</html>
事實上,它是把後面的事件流都打斷了。而如果 e.stopPropagation() 是寫在 console.log("捕獲階段觸發目標") 或者 console.log("冒泡觸發目標")後面,目標階段都點擊事件都會執行,執行按你寫的順序來。
Propagation英文意思是‘傳播’ stopPropagation 就是阻止事件傳播,阻止事件流的繼續往下發生。
所以stopPropagation()方法事實上是打斷了事件流繼續執行,而我們一般時候是直接寫在目標事件的點擊函數裏,就起到了阻止冒泡的作用!
js的事件流你真的弄明白了嗎?