JavaScript基礎概念之----事件冒泡/捕獲/委託

事件流javascript

若是單擊了頁面中的某個按鈕,同時也單擊了按鈕的容器元素,甚至單擊了整個頁面。java

IE提出的是冒泡流,網景提出的是捕獲流。node

<div id="content">content
        <div id="btn">button</div>
    </div>

    <script type="text/javascript">
        var content = document.getElementById("content");
        var btn = document.getElementById('btn');
        btn.onclick = function(){
            alert("btn");
        };
        content.onclick = function(){
            alert("content");
        };
        document.onclick = function(){
            alert("document");
        }
    </script>

//點擊容器 #btn ,則彈出的順序是:btn > content > document
//點擊容器 #content,則彈出的順序是:content > document
//點擊容器 document,則彈出的是 document

JS事件流原理圖 :app

一、一個完整的事件流是從 window 開始,最後回到 window 的一個過程測試

二、事件流被分爲三個階段:(1-5)捕獲過程、(5-6)目標過程、(6-10)冒泡過程this

<div id="wrapDiv">wrapDiv
        <p id="innerP">innerP
            <span id="textSpan">textSpan</span>
        </p>
    </div>
    <script>
    var wrapDiv = document.getElementById("wrapDiv");
    var innerP = document.getElementById("innerP");
    var textSpan = document.getElementById("textSpan");

    // 捕獲階段綁定事件
    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);

    // 冒泡階段綁定的事件
    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>

若是點擊textSpan元素,控制檯打印以下圖:spa

如上圖所示,事件傳播的過程是先捕獲,再冒泡。code

 那麼,若是不使用addEventListener方法綁定的事件(如onclick),會發生在哪一個階段?blog

<div id="wrapDiv">wrapDiv
        <p id="innerP">innerP
            <span id="textSpan">textSpan</span>
        </p>
    </div>
    <script>
    var wrapDiv = document.getElementById("wrapDiv");
    var innerP = document.getElementById("innerP");
    var textSpan = document.getElementById("textSpan");

// 測試直接綁定的事件到底發生在哪一個階段
    wrapDiv.onclick = function(){
        console.log("wrapDiv onclick 測試直接綁定的事件到底發生在哪一個階段")
    };

    // 捕獲階段綁定事件
    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);

    // 冒泡階段綁定的事件
    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>

點擊textSpan元素,以下圖控制檯輸出:事件

一、全部在目標元素上綁定的事件,都會發生在目標階段

二、在綁定捕獲代碼以前寫了綁定的冒泡階段的代碼,因此在目標元素上就不會遵照先捕獲後冒泡這一規則,而是先綁定的事件先發生。

3、因爲wrapDiv不是目標元素,因此它上面綁定的事件會遵照先捕獲後冒泡的規則。因此用onclick直接綁定的事件發生在了冒泡階段。

 事件委託

 對「事件處理程序過多」問題的解決方案就是事件委託

 事件委託利用了事件冒泡,只指定一個事件處理程序,就能夠管理某一類型的全部事件。

 例如,click事件會一直冒泡到document層次。咱們能夠爲整個頁面指定一個onclick事件處理程序,而沒必要給每一個可單擊的元素分別添加事件處理程序。

//點擊li元素,輸出li當中的顏色
<ul id="box">
    <li>red</li>
    <li>yellow</li>
    <li>blue</li>
    <li>green</li>
    <li>black</li>
    <li>white</li>
</ul>

//通常會這樣寫
<script>
    var colorBox = document.getElementById('box')
    var colors = colorBox.getElementsByTagName('li')
    for(var i=0;i<colors.length;i++){
        colors[i].addEventListener('click',function(e){
            var li = e.target;
            console.log(li.innerHTML)
        },false)
    }
</script>


//使用事件委託去寫
<script>
    var colorBox = document.getElementById('box')
    colorBox.addEventListener('click',function(e){
        var li = e.target;
        if(li.nodeName.toLowerCase() === 'li'){
            console.log(li.innerHTML)
        }
    },false)
</script>    

事件委託還有一個好處就是添加進來的元素也能綁定事件

 

//點擊li元素,輸出li當中的顏色
<ul id="box">
    <li>red</li>
    <li>yellow</li>
    <li>blue</li>
    <li>green</li>
    <li>black</li>
    <li>white</li>
</ul>
<button onclick="add()">add</button>

<script>
    var colorBox = document.getElementById('box')
    colorBox.addEventListener('click',function(e){
        var li = e.target || e.srcElement; //兼容處理
        if(li.nodeName.toLowerCase() === 'li'){
            console.log(li.innerHTML)
        }
    },false)

    function add(){
        var liNode = document.createElement('li')
        var textNode = document.createTextNode('this is add text.')
        liNode.appendChild(textNode);
        document.getElementById('box').appendChild(liNode)
    }
</script>    
相關文章
相關標籤/搜索