js事件冒泡,事件捕獲,事件委託

先來張圖理解一下事件冒泡,時間捕獲

冒泡:當事件在某一DOM元素被觸發時,例如用戶在客戶名字節點上點擊鼠標,事件將跟隨着該節點,繼承各自的父節點冒泡穿過整個的DOM節點層次,直到它遇到依附有該事件類型處理器的節點,此時,該事件是onclick事件。在冒泡過程中的任何時候都可以終止事件的冒泡,在遵從W3C標準的瀏覽器裏可以通過調用事件對象上的stopPropagation()方法,在Internet Explorer裏可以通過設置事件對象的cancelBubble屬性爲true。如果不停止事件的傳播,事件將一直通過DOM冒泡直至到達文檔根。默認情況下,事件使用冒泡事件流,不使用捕獲事件流

捕獲:事件的處理將從DOM層次的根開始,而不是從觸發事件的目標元素開始,事件被從目標元素的所有祖先元素依次往下傳遞。在這個過程中,事件會被從文檔根到事件目標元素之間各個繼承派生的元素所捕獲,如果事件監聽器在被註冊時設置了useCapture屬性爲true,那麼它們可以被分派給這期間的任何元素以對事件做出處理;否則,事件會被接着傳遞給派生元素路徑上的下一元素,直至目標元素。事件到達目標元素後,它會接着通過DOM節點再進行冒泡。

    <style>
        .block1 {
            background-color: red;
            width: 500px;
            height: 300px;
        }
        .block2 {
            background-color: blue;
            width: 500px;
            height: 200px;
        }
        .block3 {
            background-color: green;
            width: 500px;
            height: 100px;
        }
    </style>
</head>
<body>
    <div class="block1">
        <div class="block2">
            <div class="block3">

            </div>          
        </div>
    </div>
    <script type="text/javascript">
        var block = document.getElementsByTagName('div');

冒泡

block[0].addEventListener("click",function(){
            alert(1);
        },false);
        block[1].addEventListener("click",function(){
            alert(2);
        },false);
        block[2].addEventListener("click",function(){
            alert(3);
        },false);
捕獲
        block[0].addEventListener("click",function(){
            alert(1);
        },true);
        block[1].addEventListener("click",function(){
            alert(2);
        },true);
        block[2].addEventListener("click",function(){
            alert(3);
        },true);
        </script>
</body>

再來說一下dom事件流,(事件流指的是從頁面接收事件的順序。)
DOM事件流:將事件分爲三個階段:捕獲階段、目標階段、冒泡階段。先調用捕獲階段的處理函數,其次調用目標階段的處理函數,最後調用冒泡階段的處理函數

        block[0].addEventListener("click",function(){
            alert(1);
        },false);
        block[1].addEventListener("click",function(){
            alert(2);
        },false);
        block[2].addEventListener("click",function(){
            alert(3);
        },false);
        block[0].addEventListener("click",function(){
            alert(1);
        },true);
        block[1].addEventListener("click",function(){
            alert(2);
        },true);

這段代碼在點擊最內層的也就是綠色的時候,會彈出1,2(捕獲),3(處於目標狀態),2,1(冒泡)。而且被點擊元素設爲冒泡或者捕獲(第三個參數爲true 或false)是不影響的。
阻止事件冒泡和捕獲

        block[2].addEventListener("click",function(event){
            alert(3);
            event.stopPropagation();
        },false);

將上面的代碼改稱這個,即在處於目標狀態時阻止後面的冒泡進行,所以只會輸出1,2,3.

事件委託:
先借用網上的例子講一下:有三個同事預計會在週一收到快遞。爲簽收快遞,有兩種辦法:一是三個人在公司門口等快遞;二是委託給前臺MM代爲簽收。現實當中,我們大都採用委託的方案(公司也不會容忍那麼多員工站在門口就爲了等快遞)。前臺MM收到快遞後,她會判斷收件人是誰,然後按照收件人的要求籤收,甚至代爲付款。這種方案還有一個優勢,那就是即使公司裏來了新員工(不管多少),前臺MM也會在收到寄給新員工的快遞後覈實並代爲簽收。
其中我們要特別注意在新員工進來後也要享受可以代收快遞。

再看看事件委託的原理:
事件委託是利用事件的冒泡原理來實現的,何爲事件冒泡呢?就是事件從最深的節點開始,然後逐步向上傳播事件,舉個例子:頁面上有這麼一個節點樹,div>ul>li>a;比如給最裏面的a加一個click點擊事件,那麼這個事件就會一層一層的往外執行,執行順序a>li>ul>div,有這樣一個機制,那麼我們給最外面的div加點擊事件,那麼裏面的ul,li,a做點擊事件的時候,都會冒泡到最外層的div上,所以都會觸發,這就是事件委託,委託它們父級代爲執行事件。
首先想一下爲什麼用事件委託

    <ul id="uli">
        <li>1</li>
        <li>2</li>
        <li>3</li>
    </ul>
    <script type="text/javascript">
        var ul = document.getElementById('uli')
        var li = ul.getElementsByTagName('li');
        for (var i = 0; i < li.length; i++) {
            li[i].onclick = function(){
                alert(123);
            };
        }
    </script>

先看看上面的代碼,相信能看到這裏的人一定能理解上面的代碼,上面的代碼每次點擊的時候都要找一遍ul,在遍歷一次li;
這樣會增加很多的dom操作。

        var ul = document.getElementById('uli')
        ul.onclick = function(){
            alert(124);
        }

再來看看這段代碼,因爲我們在最外面加了一個點擊事件,所以在內層點擊時,會冒泡到最外層,也就是會委託父級代爲執行。
細心的同學會發現再點擊的不是li元素但是在ul內也會觸發onclick事件,但是這就與我們最初的想法有分歧了。

    <script type="text/javascript">
        var ul = document.getElementById('uli');
        ul.onclick = function(event){
            var event = event || window.event;
            var target = event.target;
            if(target.tagName.toLowerCase() == 'li')
            {
                alert(124);
            }
        }; 
    </script>

這段代碼在我們點擊的時候會判斷點擊的是不是li元素。
解決了這個問題,接下來該解決新員工的問題了,肯定有人想新員工進來不應該直接有這個屬性嗎,下面給出一段代碼自己試一下

    <ul id="uli">
        <li>1</li>
        <li>2</li>
        <li>3</li>
    </ul>
    <button id="button">add</button>
    <script type="text/javascript">
        var ul = document.getElementById('uli');
        var li = ul.getElementsByTagName('li');
        var button = document.getElementById('button');
        for(var i=0; i<li.length;i++){
                li[i].onmouseover = function(){
                    this.style.background = 'red';
                };
                li[i].onmouseout = function(){
                    this.style.background = '#fff';
                };
            }
            //添加新節點
            button.onclick = function(){
                var oLi = document.createElement('li');
                oLi.innerText = '5';
                ul.appendChild(oLi);
            };
    </script>

試過的小夥伴應該會發現新加的li是沒有移入變紅的,
再說下一般的解決方式:

            function mHover () {
                //鼠標移入變紅,移出變白
                for(var i=0; i<li.length;i++){
                    li[i].onmouseover = function(){
                        this.style.background = 'red';
                    };
                    li[i].onmouseout = function(){
                        this.style.background = '#fff';
                    }
                }
            }

但是這個又添加了一個dom操作。
再看看事件委託怎麼解決:

           ul.onmouseover = function(ev){
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLowerCase() == 'li'){
                    target.style.background = "red";
                }

            };
            ul.onmouseout = function(ev){
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLowerCase() == 'li'){
                    target.style.background = "#fff";
                }

            };

            button.onclick = function(){
                var oLi = document.createElement('li');
                ul.appendChild(oLi);
            };

有興趣的小夥伴可以去試試。或者還不理解或者有疑問的可以直接提問也可以去看看這篇博客http://www.cnblogs.com/liugang-vip/p/5616484.html

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