1. 程式人生 > >JS中的事件委託(事件代理)

JS中的事件委託(事件代理)

一步一步來說說事件委託(或者有的資料叫事件代理)

  1. js中事件冒泡我們知道,子元素身上的事件會冒泡到父元素身上。
  2. 事件代理就是,本來加在子元素身上的事件,加在了其父級身上。
  3. 那就產生了問題:父級那麼多子元素,怎麼區分事件本應該是哪個子元素的?
  4. 答案是:event物件裡記錄的有“事件源”,它就是發生事件的子元素。
  5. 它存在相容性問題,在老的IE下,事件源是 window.event.srcElement,其他瀏覽器是 event.target
  6. 用事件委託有什麼好處呢?
  7. 第一個好處是效率高,比如,不用for迴圈為子元素新增事件了
  8. 第二個好處是,js新生成的子元素也不用新為其新增事件了,程式邏輯上比較方便

--------------------

好吧,下面還是用例子來說,更容易理解。

---------------------

例子1. 頁面有個ul包含著4個li,滑鼠移動到li上,li背景變成紅色,移出,背景恢復原色。

如果按照以前的寫法,程式碼如下:

 

 1         <ul id="ul1">
 2             <li>111</li>
 3             <li>222</li>
 4             <li>333</li>
 5             <
li>444</li> 6 </ul> 7 8 <script type="text/javascript"> 9 window.onload = function(){ 10 var oUl = document.getElementById('ul1'); 11 var aLi = oUl.children; 12 console.log(aLi); 13 14
//傳統方法,li身上新增事件,需要用for迴圈,找到每個li 15 for (var i=0;i<aLi.length;i++) { 16 aLi[i].onmouseover = function() { 17 this.style.background = 'red'; 18 } 19 aLi[i].onmouseout = function(){ 20 this.style.background = ''; 21 } 22 }//for結束 23 24 25 } 26 </script>

 

現在用事件委託的方式,onmouseover、onmouseout方法要加在ul身上了,再通過找事件源的方式,改變li背景,程式碼如下:

 上面ul的html程式碼不變,js部分變為

 1 <script type="text/javascript">
 2             window.onload = function(){
 3                 var oUl = document.getElementById('ul1');                
 4                 oUl.onmouseover = function(ev){
 5                     var ev = ev || window.event;
 6                     var oLi = ev.srcElement || ev.target;
 7                     oLi.style.background = 'red';                    
 8                 }
 9                 
10                 oUl.onmouseout = function(ev){
11                     var ev = ev || window.event;
12                     var oLi = ev.srcElement || ev.target;
13                     oLi.style.background = '';                    
14                 }
15                 
16             }
17         </script>

 

效果如下:

 

但是會發現,滑鼠移到了ul身上而不是某個li身上時,獲取的事件源是ul,那麼整個ul背景將變紅,這不是想要的結果,怎麼辦?

答曰:加個判斷。通過事件源的nodeName判斷是不是li,是才做出反應,不是不理它。為了防止nodeName在不同瀏覽器獲取的字母大小寫不同,加個toLowerCase()

所以,上面的js程式碼更改如下:

 1         <script type="text/javascript">
 2             window.onload = function(){
 3                 var oUl = document.getElementById('ul1');
 4                 
 5                 oUl.onmouseover = function(ev){
 6                     var ev = ev || window.event;
 7                     var oLi = ev.srcElement || ev.target;
 8                     if(oLi.nodeName.toLowerCase() == 'li'){
 9                         oLi.style.background = 'red';
10                     }
11                                         
12                 }
13                 
14                 oUl.onmouseout = function(ev){
15                     var ev = ev || window.event;
16                     var oLi = ev.srcElement || ev.target;
17                     if(oLi.nodeName.toLowerCase() == 'li'){
18                         oLi.style.background = '';
19                     }                
20                 }
21 
22                 
23             }
24         </script>
25         

 

效果如下很完美:

這就是不用for迴圈寫一堆了,下面再來說說第二個好處:js新生成的子元素也不用新為其新增事件了,程式邏輯上比較方便

上面的檔案,假如我再新添加個按鈕,點選按鈕,ul裡就新增加個li,如果用傳統的方法,for迴圈為li新增事件,問題就出現了,最開始有的4個li是有onmouseover和onmouseout事件的,但是後來動態生成的li裡沒有這兩個事件處理函式,因為for迴圈的時候它還沒生成。怎麼辦呢?只能在按鈕點選後,生成li,然後再為生成的li再繫結事件,真是麻煩的很。而事件委託的方式就沒事,當後來動態生成的li出現的時候,不用做改變,移到它身上,還是變色的,因為事件是繫結在ul身上的。

新浪微博裡,當你發一條新微博出去,發出去的資訊在頁面上顯示,滑鼠移動到新發的資訊的人頭像上時,依然會有很多事件,如果用原來的方式,就要做很多處理,事件委託的話,就很方便了!