1. 程式人生 > >js的事件流你真的弄明白了嗎?

js的事件流你真的弄明白了嗎?

版本 false () func chrom ner 瀏覽器 relative 什麽

當瀏覽器發展到第四代時候,瀏覽器開發團隊遇到了一個有意思的問題;頁面的哪一部分會擁有某個特地的事件?要明白這個問題問的是什麽,可以想象畫在紙上的一組同心圓,如果你把手指放在圓心上,那麽你的手指指向的不是一個圓,而是紙上所有的圓。兩家公司的開發團隊在這件事情上的看法是一致的,如果你單擊了某個按鈕,他們都認為單擊事件不僅發生在按鈕上,更是發生在了整個容器元素,甚至是整個頁面。

那麽對於頁面接收事件的順序兩者出現了不同的定義:捕獲流 ( 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的事件流你真的弄明白了嗎?