事件(五):事件委託

什麼是事件委託:html

高級程序設計裏面:事件委託就是利用事件冒泡,只指定一個事件處理程序,就能夠管理某一類型的全部事件node

通俗的講,事件就是onclick,onmouseover,onmouseout,等就是事件,委託呢,就是讓別人來作,這個事件原本是加在某些元素上的,然而你卻加到別人身上來作,完成這個事件。瀏覽器

也就是:利用冒泡的原理,把事件加到父級上,觸發執行效果app

 

優勢:dom

1.不須要爲每個元素都添加監聽事件而是經過委託給父元素來處理。這樣就減小了內存,性能提升了函數

2.能夠方便動態添加元素。不須要再爲新添加的元素從新綁定事件。性能

 

咱們能夠看一個例子:須要觸發每一個li來改變他們的背景顏色優化

<ul id="ul">
  <li>aaaaaaaa</li>
  <li>bbbbbbbb</li>
  <li>cccccccc</li>
</ul>

 

window.onload = function(){
  var oUl = document.getElementById("ul");
  var aLi = oUl.getElementsByTagName("li");

  for(var i=0; i<aLi.length; i++){
    aLi[i].onmouseover = function(){
      this.style.background = "red";
    }
    aLi[i].onmouseout = function(){
      this.style.background = "";
    }
  }
}

 

缺點:可能有不少個li用for循環的話就比較影響性能。this

下面咱們能夠用事件委託的方式來實現這樣的效果。html不變spa

window.onload = function(){
    var oUl = document.getElementById('ul');
    var aLi = oUl.getElementsByTagName('li');

    /*
    * 這裏要用到事件源:event對象,事件源,無論在哪一個事件中,你操做的那個元素,就是事件源
    * ie:window.event.srcElement
    * 標準下:event.target
    * nodeName: 找到元素的標籤名
    */
    oUl.onmouseover = function(event){
        debugger;
        var ev = event || window.event;
        var target = ev.target || ev.srcElement
        if(target.nodeName.toLowerCase() == 'li'){
            target.style.background = 'red';
        }
    }
    oUl.onmouseout = function(event){
        debugger;
        var ev = event || window.event;
        var target = ev.target || ev.srcElement;
        if(target.nodeName.toLowerCase() == 'li'){
            target.style.background = '';
        }
    }
}

 

咱們還拿這個例子看,可是咱們要作動態的添加li。點擊button動態添加li

<input type="button" id="btn" />
<ul id="ul">
  <li>aaaaaaaa</li>
  <li>bbbbbbbb</li>
  <li>cccccccc</li>
</ul>

 

不用事件委託咱們會這樣作:

 window.onload = function(){
  var oUl = document.getElementById("ul");
  var aLi = oUl.getElementsByTagName("li");
  var oBtn = document.getElementById("btn");
  var iNow = 4;
  for(var i=0; i<aLi.length; i++){
    aLi[i].onmouseover = function(){
      this.style.background = "red";
    }
    aLi[i].onmouseout = function(){
      this.style.background = "";
    }
  }

  oBtn.onclick = function(){
    iNow ++;
    var oLi = document.createElement("li");
    oLi.innerHTML = 1111 *iNow;
    oUl.appendChild(oLi);
  }
}

 

缺點:點擊按鈕新加的li上面沒有鼠標移入事件來改變他們的背景顏色
window.onload = function(){
  var oUl = document.getElementById("ul");
  var aLi = oUl.getElementsByTagName("li");
  var oBtn = document.getElementById("btn");
  var iNow = 4;

  oUl.onmouseover = function(ev){
    var ev = ev || window.event;
    var target = ev.target || ev.srcElement;
    //alert(target.innerHTML);
    if(target.nodeName.toLowerCase() == "li"){
    target.style.background = "red";
    }
  }
  oUl.onmouseout = function(ev){
    var ev = ev || window.event;
    var target = ev.target || ev.srcElement;
    //alert(target.innerHTML);
    if(target.nodeName.toLowerCase() == "li"){
    target.style.background = "";
    }
  }
  oBtn.onclick = function(){
    iNow ++;
    var oLi = document.createElement("li");
    oLi.innerHTML = 1111 *iNow;
    oUl.appendChild(oLi);
  }
}

 除了提升性能和節省內存的好處外,事件委託的另外一個好處在於,頁面動態變化後,不須要從新檢索元素和綁定事件

 

 上面的例子是說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 (event) {
         var ev = event || 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操做就能完成全部的效果,比上面的性能確定是要好一些的

 

因爲事件處理程序能夠爲現代 Web 應用程序提供交互能力,所以許多開發人員會不分青紅皁白地 向頁面中添加大量的處理程序

在 JavaScript 中,添加到頁面上 的事件處理程序數量將直接關係到頁面的總體運行性能。致使這一問題的緣由是多方面的。首先,每一個 函數都是對象,都會佔用內存;內存中的對象越多,性能就越差。其次,必須事先指定全部事件處理程 序而致使的 DOM 訪問次數,會延遲整個頁面的交互就緒時間

內存中留有那 些過期不用的「空事件處理程序」(dangling event handler),也是形成 Web 應用程序內存與性能問題的 主要緣由。

 

兩種狀況下,可能會形成上述問題

一、從文檔中移除帶有事件處理程序的元素時。 這多是經過純粹的 DOM 操做,例如使用 removeChild()和 replaceChild()方法

二、更多地是發 生在使用 innerHTML 替換頁面中某一部分的時候。若是帶有事件處理程序的元素被 innerHTML 刪除 了,那麼原來添加到元素中的事件處理程序極有可能沒法被看成垃圾回收

<div id="myDiv">
    <input type="button" value="Click Me" id="myBtn">
</div>

 

<script>
   var btn = document.getElementById("myBtn");
   btn.onclick = function(){
       //先執行某些操做
       document.getElementById("myDiv").innerHTML = "Processing..."; //麻煩了!
   };
</script>

 

但問題在於,當按鈕被從頁面中移除時,它還帶着一個 事件處理程序呢。在元素上設置 innerHTML 能夠把按鈕移走,但事件處理程序仍然與按鈕保持 着引用關係。有的瀏覽器(尤爲是 IE)在這種狀況下不會做出恰當地處理,它們頗有可能會將對元素和 對事件處理程序的引用都保存在內存中。若是你知道某個元素即將被移除,那麼最好手工移除事件處理 程序

 

<script>
   var btn = document.getElementById("myBtn");

    btn.onclick = function(){
        //先執行某些操做
        btn.onclick = null; //移除事件處理程序
        document.getElementById("myDiv").innerHTML = "Processing..."; //麻煩了!
    };
</script>

 

在此,咱們在設置div的 innerHTML 屬性以前,先移除了按鈕的事件處理程序。這樣就確保了 內存能夠被再次利用,而從 DOM 中移除按鈕也作到了乾淨利索。

致使「空事件處理程序」的另外一種狀況,就是卸載頁面的時候。絕不奇怪,IE8 及更早版本在這種 狀況下依然是問題最多的瀏覽器,儘管其餘瀏覽器或多或少也有相似的問題。若是在頁面被卸載以前沒 有清理乾淨事件處理程序,那它們就會滯留在內存中。每次加載完頁面再卸載頁面時(多是在兩個頁 19 面間來回切換,也能夠是單擊了「刷新」按鈕),內存中滯留的對象數目就會增長,由於事件處理程序 佔用的內存並無被釋放。 通常來講,最好的作法是在頁面卸載以前,先經過 onunload 事件處理程序移除全部事件處理程序。  在此,事件委託技術再次表現出它的優點——須要跟蹤的事件處理程序越少,移除它們就越容易。對這 種相似撤銷的操做,咱們能夠把它想象成:只要是經過 onload 事件處理程序添加的東西,最後都要通 過 onunload 事件處理程序將它們移除。

 

總結:

那什麼樣的事件能夠用事件委託,什麼樣的事件不能夠用呢?

適合用事件委託的事件:click,mousedown,mouseup,keydown,keyup,keypress。

值得注意的是,mouseover和mouseout雖然也有事件冒泡,可是處理它們的時候須要特別的注意,由於須要常常計算它們的位置,處理起來不太容易。

不適合的就有不少了,舉個例子,mousemove,每次都要計算它的位置,很是很差把控,在不如說focus,blur之類的,自己就沒用冒泡的特性,天然就不能用事件委託了

委託缺點:

  1. 事件委託基於冒泡,對於不冒泡的事件不支持。
  2. 層級過多,冒泡過程當中,可能會被某層阻止掉。
  3. 理論上委託會致使瀏覽器頻繁調用處理函數,雖然極可能不須要處理。因此建議就近委託,好比在table上代理td,而不是在document上代理td。
  4. 把全部事件都用代理就可能會出現事件誤判。好比,在document中代理了全部button的click事件,另外的人在引用改js時,可能不知道,形成單擊button觸發了兩個click事件。
相關文章
相關標籤/搜索