細說事件流

      相信大家對事件流有一點的認識,在這裏我就通過一個例子來說明其中的細節。這是你知道的不知道的,不知道的知道的,笑死。

一、事件階段:

      捕獲階段 目標階段 冒泡階段

二、案例分析

1.html部分

<div id='wrapDiv'>wrapDiv
     <p id="innerP">innerP
         <span id="textSpan">textSpan</span>
     </p>
</div>

2.css部分

 <style>
        #wrapDiv,
        #innerP,
        #textSpan {
            box-sizing: border-box;
            /**width+padding+border**/
            margin: 40px;
            cursor: pointer;
        }

        #wrapDiv {
            width: 300px;
            height: 300px;
            border: crimson solid 3px;
        }

        #innerP {
            width: 200px;
            height: 200px;
            border: green solid 3px;
        }

        #textSpan {
            width: 100px;
            height: 100px;
            border: blue solid 3px;
            display: block;
        } 
</style>

3.js部分

        var wrapDiv=document.getElementById('wrapDiv');
        var innerP=document.getElementById('innerP');
        var textSpan=document.getElementById('textSpan');
        // Event.currentTarget指向事件綁定的元素,而 Event.target 則是事件觸發的元素。


        // true 表示捕獲階段
        window.addEventListener('click',function(e){
            console.log('window捕獲',e.target.nodeName,e.currentTarget.nodeName);
        },true);
        document.addEventListener('click',function(e){
            console.log('document捕獲',e.target.nodeName,e.currentTarget.nodeName);
        },true);
        document.documentElement.addEventListener('click',function(e){
            console.log('documentElement捕獲',e.target.nodeName,e.currentTarget.nodeName);
        },true);
        document.body.addEventListener('click',function(e){
            console.log('body捕獲',e.target.nodeName,e.currentTarget.nodeName);
        },true);
        wrapDiv.addEventListener('click',function(e){
            console.log('wrapDiv捕獲',e.target.nodeName,e.currentTarget.nodeName);
        },true);
        innerP.addEventListener('click',function(e){
            console.log('innerP捕獲',e.target.nodeName,e.currentTarget.nodeName);
        },true);
        textSpan.addEventListener('click',function(e){
            console.log('textSpan捕獲',e.target.nodeName,e.currentTarget.nodeName);
        },true);
        
        // false 表示冒泡階段
        window.addEventListener('click',function(e){
            console.log('window冒泡',e.target.nodeName,e.currentTarget.nodeName);
        },false);
        document.addEventListener('click',function(e){
            console.log('document冒泡',e.target.nodeName,e.currentTarget.nodeName);
        },false);
        document.documentElement.addEventListener('click',function(e){
            console.log('documentElement冒泡',e.target.nodeName,e.currentTarget.nodeName);
        },false);
        document.body.addEventListener('click',function(e){
            console.log('body冒泡',e.target.nodeName,e.currentTarget.nodeName);
        },false);
        wrapDiv.addEventListener('click',function(e){
            console.log('wrapDiv冒泡',e.target.nodeName,e.currentTarget.nodeName);
        },false);
        innerP.addEventListener('click',function(e){
            console.log('innerP冒泡',e.target.nodeName,e.currentTarget.nodeName);
        },false);
        textSpan.addEventListener('click',function(e){
            console.log('textSpan冒泡',e.target.nodeName,e.currentTarget.nodeName);
        },false);

4.頁面效果

問題來了來了!!!

(1).當點擊span結果會是什麼?(先捕獲後冒泡 順序看打印結果)

 

(2).如果將目標元素的冒泡和捕獲綁定順序互換 (打印結果出了目標元素的冒泡和捕獲是根據綁定的順序有關(誰先綁定誰先觸發),所以在目標階段 不一定是先捕獲後冒泡

        // true 表示捕獲階段
        window.addEventListener('click', function (e) {
            //window無nodeName所以打印出來是undefined,不要見怪哦。
            console.log('window捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);
        document.addEventListener('click', function (e) {
            console.log('document捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);
        document.documentElement.addEventListener('click', function (e) {
            console.log('documentElement捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);
        document.body.addEventListener('click', function (e) {
            console.log('body捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);
        wrapDiv.addEventListener('click', function (e) {
            console.log('wrapDiv捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);
        innerP.addEventListener('click', function (e) {
            console.log('innerP捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);
        textSpan.addEventListener('click', function (e) {
            console.log('textSpan冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);

        // 將被點擊元素提前
        textSpan.addEventListener('click', function (e) {
            console.log('textSpan捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);

        // false 表示冒泡階段
        window.addEventListener('click', function (e) {
            console.log('window冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);
        document.addEventListener('click', function (e) {
            console.log('document冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);
        document.documentElement.addEventListener('click', function (e) {
            console.log('documentElement冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);
        document.body.addEventListener('click', function (e) {
            console.log('body冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);
        wrapDiv.addEventListener('click', function (e) {
            console.log('wrapDiv冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);
        innerP.addEventListener('click', function (e) {
            console.log('innerP冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);

(3).如果給元素綁定onclick呢,和addEventLister又有什麼區別呢?

A:onclick這樣的事件,給同一個元素,同一個事件綁定多個處理函數後面會覆蓋前面的

B:addEventLister 就可以實現同一個元素,同一個事件對應多個處理函數,不會覆蓋

C:onclick和addEventLister混合使用,不會影響

D.onclick可以統一看做是在冒泡階段,爲什麼這麼說看下面例子

    <script>
        var wrapDiv = document.getElementById('wrapDiv');
        var innerP = document.getElementById('innerP');
        var textSpan = document.getElementById('textSpan');
        // Event.currentTarget指向事件綁定的元素,而 Event.target 則是事件觸發的元素。
       
        // 添加onclick事件
        wrapDiv.onclick = function (e) {
            console.log('使用wrapDiv.onclick,直接綁定的元素,但不是直接觸發的元素:在冒泡階段觸發', e.target.nodeName, e.currentTarget.nodeName);
        }
        textSpan.onclick = function (e) {
            console.log('使用textSpan.onclick,既是直接綁定的元素,又是直接觸發的元素:發生在哪個階段是根據它和綁定捕獲的位置有關,誰在前誰先觸發', e.target.nodeName, e.currentTarget.nodeName);
        }

        // true 表示捕獲階段
        window.addEventListener('click', function (e) {
            console.log('window捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);
        document.addEventListener('click', function (e) {
            console.log('document捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);
        document.documentElement.addEventListener('click', function (e) {
            console.log('documentElement捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);
        document.body.addEventListener('click', function (e) {
            console.log('body捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);
        wrapDiv.addEventListener('click', function (e) {
            console.log('wrapDiv捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);
        innerP.addEventListener('click', function (e) {
            console.log('innerP捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);
        textSpan.addEventListener('click', function (e) {
            console.log('textSpan捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);


        // false 表示冒泡階段
        window.addEventListener('click', function (e) {
            console.log('window冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);
        document.addEventListener('click', function (e) {
            console.log('document冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);
        document.documentElement.addEventListener('click', function (e) {
            console.log('documentElement冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);
        document.body.addEventListener('click', function (e) {
            console.log('body冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);
        wrapDiv.addEventListener('click', function (e) {
            console.log('wrapDiv冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);
        innerP.addEventListener('click', function (e) {
            console.log('innerP冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);
        textSpan.addEventListener('click', function (e) {
            console.log('textSpan冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);    
    </script>

(4).阻止事件傳播(e.stopPropagation();阻止冒泡,其實也可以阻止捕獲

        // true 表示捕獲階段
        window.addEventListener('click', function (e) {
            console.log('window捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);
        document.addEventListener('click', function (e) {
            console.log('document捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);
        document.documentElement.addEventListener('click', function (e) {
            console.log('documentElement捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);
        document.body.addEventListener('click', function (e) {
            console.log('body捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);
        wrapDiv.addEventListener('click', function (e) {
            console.log('wrapDiv捕獲', e.target.nodeName, e.currentTarget.nodeName);
            //在捕獲階段阻止事件的傳播
            e.stopPropagation();
            // 或使用
            // e.cancelBubble = true;  //這個屬性的規範並未統一 少用,知道就行

        }, true);
        innerP.addEventListener('click', function (e) {
            console.log('innerP捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);
        textSpan.addEventListener('click', function (e) {
            console.log('textSpan捕獲', e.target.nodeName, e.currentTarget.nodeName);
        }, true);


        // false 表示冒泡階段
        window.addEventListener('click', function (e) {
            console.log('window冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);
        document.addEventListener('click', function (e) {
            console.log('document冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);
        document.documentElement.addEventListener('click', function (e) {
            console.log('documentElement冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);
        document.body.addEventListener('click', function (e) {
            console.log('body冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);
        wrapDiv.addEventListener('click', function (e) {
            console.log('wrapDiv冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);
        innerP.addEventListener('click', function (e) {
            console.log('innerP冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false);
        textSpan.addEventListener('click', function (e) {
            console.log('textSpan冒泡', e.target.nodeName, e.currentTarget.nodeName);
        }, false); 

 

(5).拓展:js中preventDefault()阻止事件的默認行爲,而不是在阻止冒泡

// 默認行爲是指:點擊a標籤就跳轉,拖拽一個圖片到瀏覽器會自動打開,點擊表單的提交按鈕會提交表單
     e.preventDefault();
     //或
    //return false;

(6).addEventListener兼容問題(使用attachEvent)

if (el.addEventListener) {
  el.addEventListener('click', modifyText, false); 
} else if (el.attachEvent)  { //兼容IE9之前
  el.attachEvent('onclick', modifyText);//無第三個參數
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章