js 事件委託 事件代理

js 事件委託 事件代理

JavaScript高級程序設計上解釋:事件委託就是利用事件冒泡,只指定一個事件處理程序,就能夠管理某一類型的全部事件。javascript

經過例子類比:

有三個同事預計會在週一收到快遞。爲簽收快遞,有兩種辦法:一是三我的在公司門口等快遞;二是委託給前臺MM爲簽收。現實中,咱們大都採用委託的方案。前臺MM收到快遞後,會判斷收件人是誰,而後按照收件人的要求籤收。這種方案的優點就是不管有多少個新員工要收快遞,前臺MM均可以代爲簽收。 這裏有兩層意思: 第一,如今委託前臺的同事是能夠代爲簽收的,即程序中的現有的DOM節點是有事件的; 第二,新員工也是能夠被前臺MM代爲簽收的,即程序中新添加的DOM節點也是有事件的。java

爲何要有事件委託:

若是不少個DOM須要添加事件處理,好比100個li都有相同的click點擊事件,可能咱們會用for循環的方法,來遍歷全部的li,而後給它們添加事件。在JavaScript中,添加到頁面上的事件處理程序數量將直接關係到頁面的總體運行性能,由於須要不斷的與DOM節點進行交互,訪問DOM的次數越多,引發瀏覽器重繪與重排的次數也就越多,就會延長整個頁面的交互就緒時間,這就是性能優化要減小DOM操做的緣由:若是用事件委託,就會將全部的操做放到js程序裏,與DOM的操做就只需交互一次,這樣就能減小與DOM的交互次數,提升性能; 每一個函數都是一個對象,是對象就會佔用內存,對象越多,內存佔用率就越大,性能也就會相應降低。好比上面的100個li,就要佔用100個內存空間,若是是1000,10000個,性能影響更加明顯。若是採用事件委託,就只需對它的父級(若是隻有一個父級)這一個對象進行操做,咱們就只須要一個內存空間就夠了,性能也會相應的提升。node

事件委託原理:

事件委託是利用事件的冒泡原理來實現的,事件冒泡就是事件從最深的節點開始,而後逐級向上傳播事件。舉個例子:頁面上有一個節點樹,div>ul>li>a;好比給最裏面的a加一個click點擊事件,那麼這個事件愛你就會逐層向外執行,執行順序爲a>li>ul>div,事件委託的機制就在於咱們能夠給最外面的div加點擊事件,那麼裏面的ul,li,a作點擊事件的時候,都會冒泡到最外層的div上,委託它們的父級代爲執行事件。瀏覽器

事件委託怎麼實現:

首先看一個例子: 子節點實現相同的功能:性能優化

<ul id="ul1">
    <li>111</li>
    <li>222</li>
    <li>333</li>
    <li>444</li>
</ul>
複製代碼

實現的功能是點擊li,彈出123app

window.onload = function(){
    var oUl = document.getElementById("ul1");
    var aLi = oUl.getElementsByTagName('li');
    for(var i=0;i<aLi.length;i++){
        aLi[i].onclick = function(){
            alert(123);
        }
    }
}
複製代碼

上面代碼首先找到ul,而後遍歷li,而後點擊li的時候,又要找一次目標的li的位置,才能執行最後的操做,每次點擊都要找一次li;函數

若是用事件委託的方式作呢:性能

window.onload = function(){
    var oUl = document.getElementById("ul1");
   oUl.onclick = function(){
        alert(123);
    }
}
複製代碼

這裏用父級ul作事件處理,當li被點擊時,因爲冒泡原理,事件就會冒泡到ul上,由於ul上有點擊事件,因此事件就會觸發,可是這樣點擊ul也會觸發,解決方案: Event對象提供了一個屬性叫target,能夠返回事件的目標節點,咱們稱爲事件源。即target能夠表示爲當前的事件操做的DOM。兼容性問題:標準瀏覽器用ev.target,IE瀏覽器用event.srcElement,此時只是獲取了當前節點的位置,能夠用nodeName來獲取具體是什麼標籤名,這裏返回是大寫的,須要轉成小寫toLowerCase()來作比較.優化

window.onload = function(){
&emsp;&emsp;var oUl = document.getElementById("ul1");
&emsp;&emsp;oUl.onclick = function(ev){
&emsp;&emsp;&emsp;&emsp;var ev = ev || window.event;
&emsp;&emsp;&emsp;&emsp;var target = ev.target || ev.srcElement;
&emsp;&emsp;&emsp;&emsp;if(target.nodeName.toLowerCase() == 'li'){
&emsp; &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;    alert(123);
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;  alert(target.innerHTML);
&emsp;&emsp;&emsp;&emsp;}
&emsp;&emsp;}
}
複製代碼

這樣就只有點擊li會觸發事件,且每次只執行一次DOM操做,若是li數量不少的話,將大大減小DOM的操做,優化性能。ui

上面的例子是每一個li操做是一樣的效果,若是每一個li被點擊的效果不同呢:

<div id="box">
        <input type="button" id="add" value="添加" />
        <input type="button" id="remove" value="刪除" />
        <input type="button" id="move" value="移動" />
        <input type="button" id="select" value="選擇" />
 </div>
複製代碼
window.onload = function(){
            var Add = document.getElementById("add");
            var Remove = document.getElementById("remove");
            var Move = document.getElementById("move");
            var Select = document.getElementById("select");
            
            Add.onclick = function(){
                alert('添加');
            };
            Remove.onclick = function(){
                alert('刪除');
            };
            Move.onclick = function(){
                alert('移動');
            };
            Select.onclick = function(){
                alert('選擇');
            }
            
        }
複製代碼

上面代碼4個按鈕,點擊每個作不一樣的操做,至少須要4次DOM操做,用事件委託進行優化:

window.onload = function(){
            var oBox = document.getElementById("box");
            oBox.onclick = function (ev) {
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLocaleLowerCase() == 'input'){
                    switch(target.id){
                        case 'add' :
                            alert('添加');
                            break;
                        case 'remove' :
                            alert('刪除');
                            break;
                        case 'move' :
                            alert('移動');
                            break;
                        case 'select' :
                            alert('選擇');
                            break;
                    }
                }
            }
            
        }
複製代碼

用事件委託就只用一次DOM操做就能完成操做,提升性能

上面講的都是document加載完成的現有DOM節點下的操做,若是是新增節點的會有事件嗎?

<input type="button" name="" id="btn" value="添加" />
    <ul id="ul1"> <li>111</li> <li>222</li> <li>333</li> <li>444</li> </ul>   
複製代碼

如今是移入lili變紅,移出lili變白,這樣一個效果,而後點擊按鈕,能夠向ul中添加一個li子節點

window.onload = function(){
            var oBtn = document.getElementById("btn");
            var oUl = document.getElementById("ul1");
            var aLi = oUl.getElementsByTagName('li');
            var num = 4;
            
            //鼠標移入變紅,移出變白
            for(var i=0; i<aLi.length;i++){
                aLi[i].onmouseover = function(){
                    this.style.background = 'red';
                };
                aLi[i].onmouseout = function(){
                    this.style.background = '#fff';
                }
            }
            //添加新節點
            oBtn.onclick = function(){
                num++;
                var oLi = document.createElement('li');
                oLi.innerHTML = 111*num;
                oUl.appendChild(oLi);
            };
        }
複製代碼

執行後發現,新增的li是沒有事件的,說明在添加子節點的時候,事件沒有一塊兒添加進去。解決方案就是將for循環用一個函數包起來,命名mHover

window.onload = function(){
            var oBtn = document.getElementById("btn");
            var oUl = document.getElementById("ul1");
            var aLi = oUl.getElementsByTagName('li');
            var num = 4;
            
            function mHover () {
                //鼠標移入變紅,移出變白
                for(var i=0; i<aLi.length;i++){
                    aLi[i].onmouseover = function(){
                        this.style.background = 'red';
                    };
                    aLi[i].onmouseout = function(){
                        this.style.background = '#fff';
                    }
                }
            }
            mHover ();
            //添加新節點
            oBtn.onclick = function(){
                num++;
                var oLi = document.createElement('li');
                oLi.innerHTML = 111*num;
                oUl.appendChild(oLi);
                mHover ();
            };
        }
複製代碼

雖然功能實現了,但實際上有增長了一個DOM操做,不利於性能,用事件委託優化

window.onload = function(){
            var oBtn = document.getElementById("btn");
            var oUl = document.getElementById("ul1");
            var aLi = oUl.getElementsByTagName('li');
            var num = 4;
            
            //事件委託,添加的子元素也有事件
            oUl.onmouseover = function(ev){
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLowerCase() == 'li'){
                    target.style.background = "red";
                }
                
            };
            oUl.onmouseout = function(ev){
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLowerCase() == 'li'){
                    target.style.background = "#fff";
                }
                
            };
            
            //添加新節點
            oBtn.onclick = function(){
                num++;
                var oLi = document.createElement('li');
                oLi.innerHTML = 111*num;
                oUl.appendChild(oLi);
            };
        }
複製代碼

上面就是利用事件委託的方式,新添加的子元素是帶有事件效果的。當用事件委託的時候,不須要去遍歷元素的子節點,只須要給父級元素添加事件就能夠了,其餘的都是在js裏面執行,這樣就能夠減小DOM操做,提升性能,這也是事件委託的優勢。

適用事件委託的事件:click,mousedown,mouseup,keydown,keyup,keypress (mouseover事件:不論鼠標指針穿過被選元素或其子元素,都會觸發mouseover事件;mouseenter事件:只有鼠標指針穿過被選元素時,纔會觸發mouseenter事件。)

歡迎關注
相關文章
相關標籤/搜索