1. 程式人生 > >js事件監聽機制(事件捕獲)總結

js事件監聽機制(事件捕獲)總結

在前端開發過程中我們經常會遇到給頁面元素新增事件的問題,新增事件的js方法也很多,有直接加到頁面結構上的,有使用一些js事件監聽的方法,由於各個瀏覽器對事件冒泡事件監聽的機制不同,le瀏覽器只有事件冒泡,沒有事件監聽的機制,對於事件監聽的相容性問題是最大的難題:

1.直接把事件的方法寫在頁面結構上

[javascript] view plain copy
  1. function eventfun(){   
  2. //console.log(this); 
  3. }   
  4. <input type="button" onclick="eventfun()" value="button" />  
  5. //這裡涉及到一個this作用域的問題,eventfun再這裡是一個全域性函式, 物件是[object Window],this指向的是window.

要解決this作用域的問題,可以使用為全域性函式新增event變數的方法,在頁面結構上將this物件作為引數傳遞到函式內部使用
[javascript] view plain copy
  1. <input type="button" onclick="eventfun2(this)" value="button2" />   
  2. function eventfun2(eve){//在這裡把事件物件作為引數傳遞到全域性方法裡 
  3. eve.name="alex";   
  4. window.name="robin";   
  5. console.log(this);//[object Window] 
  6. console.log(eve);// [object HTMLInputElement] 
  7. console.log(this.name);// robin 
  8. console.log(eve.name);// alexvar 
  9. self=eve;   
  10. console.log(this.name);//robin 
  11. console.log(self.name);//alex 
  12. alert(window.name);   
  13. alert(self.name);   
  14. }  

2. 使用給事件屬性賦值的方法,是一種為事件繫結的方法,但是這種方法的侷限性就是隻能為事件繫結一個方法,如果繫結多個就會以後一個方法為準

HTMLElementobject.onclick = fucntion(){//使用這種為事件屬性賦值的方法,this的指標會指向window物件,而不是被事件物件,所以這種方法是引用 

[javascript] view plain copy
  1. //js code 
  2. fun1();   
  3. fun2();   
  4. fun3();   
  5. console.log(this);//window.object 
  6. }   
  7. function dosomething(){   
  8. //js code 
  9. }   
  10. HTMLElementobject.onclick = dosomething;//使用這種為事件物件屬性賦值的形式,this指標指向事件執行物件 
  11. console.log(this);//htmlElementObject

3.事件傳播——冒泡與捕獲
DOM事件標準定義了兩種事件流,這兩種事件流有著顯著的不同並且可能對你的應用有著相當大的影響。這兩種事件流分別是捕獲和冒泡。和許多Web技 術一樣,在它們成為標準之前,Netscape和微軟各自不同地實現了它們。Netscape選擇實現了捕獲事件流,微軟則實現了冒泡事件流。幸運的 是,W3C決定組合使用這兩種方法,並且大多數新瀏覽器都遵循這兩種事件流方式。
預設情況下,事件使用冒泡事件流,不使用捕獲事件流。然而,在Firefox和Safari裡,你可以顯式的指定使用捕獲事件流,方法是在註冊事件時傳入useCapture引數,將這個引數設為true。


冒泡事件流
當事件在某一DOM元素被觸發時,例如使用者在客戶名位元組點上點選滑鼠,事件將跟隨著該節點繼承自的各個父節點冒泡穿過整個的DOM節點層次,直到它 遇到依附有該事件型別處理器的節點,此時,該事件是onclick事件。在冒泡過程中的任何時候都可以終止事件的冒泡,在遵從W3C標準的瀏覽器裡可以通 過呼叫事件物件上的stopPropagation()方法,在Internet Explorer裡可以通過設定事件物件的cancelBubble屬性為true。如果不停止事件的傳播,事件將一直通過DOM冒泡直至到達文件根。即從被點選的元素到此元素的祖先元素直至根元素,從下到上。


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


現代事件繫結方法
使用傳統事件繫結有許多缺陷,比如不能在一個物件的相同事件上註冊多個事件處理函式。而瀏覽器和W3C也並非沒有考慮到這一點,因此在現代瀏覽器中,它們有自己的方法繫結事件。
W3C DOM
obj.addEventListener(evtype,fn,useCapture)——W3C提供的新增事件處理函式的方法。obj是要添 加事件的物件,evtype是事件型別,不帶on字首,fn是事件處理函式,如果useCapture是true,則事件處理函式在捕獲階段被執行,否則 在冒泡階段執行
obj.removeEventListener(evtype,fn,useCapture)——W3C提供的刪除事件處理函式的方法


微軟IE方法
obj.attachEvent(evtype,fn)——IE提供的新增事件處理函式的方法。obj是要新增事件的物件,evtype是事件型別,帶on字首,fn是事件處理函式,IE不支援事件捕獲
obj.detachEvent(evtype,fn,)——IE提供的刪除事件處理函式的方法,evtype包含on字首

整合兩者的方法

[javascript] view plain copy
  1. function addEvent(obj,evtype,fn,useCapture) {   
  2. if (obj.addEventListener) {   
  3. obj.addEventListener(evtype,fn,useCapture);   
  4. else {   
  5. obj.attachEvent("on"+evtype,fn);//IE不支援事件捕獲 
  6. else {   
  7. obj["on"+evtype]=fn;//事實上這種情況不會存在 
  8. }   
  9. }   
  10. function delEvent(obj,evtype,fn,useCapture) {   
  11. if (obj.removeEventListener) {   
  12. obj.removeEventListener(evtype,fn,useCapture);   
  13. else {   
  14. obj.detachEvent("on"+evtype,fn);   
  15. else {   
  16. obj["on"+evtype]=null;   
  17. }   
  18. }  

IE的attach方法有個問題,就是使用attachEvent時在事件處理函式內部,this指向了window,而不是obj!當然,這個是有解決方案的!

但IE的attachEvent方法有另外一個問題,同一個函式可以被註冊到同一個物件同一個事件上多次,解決方法:拋棄IE的 attachEvent方法吧!IE下的attachEvent方法不支援捕獲,和傳統事件註冊沒多大區別(除了能繫結多個事件處理函式),並且IE的 attachEvent方法存在記憶體洩漏問題!


addEvent,delEvent現代版

[javascript] view plain copy
  1. <body>   
  2. <div id="outEle" style="padding:10px; border:1px solid #b2b2b2; background:#efefef;">   
  3. <input type="button" onclick="eventfun()" id="button" value="button" /><br />   
  4. <input type="button" onclick="eventfun2(this);" id="button2" value="button2" /><br />   
  5. <input type="button" id="button3" value="button3" /><br />   
  6. <input type="button" id="button4" value="button4" /><br />   
  7. <table id="htmlEleTable" width="100%" border="0" style="border:1px solid #b2b2b2; background:#fff;">   
  8. <tr id="1111"><td>111111111111111111111111111111</td></tr>   
  9. <tr id="22222"><td>222222222222222222222222222222</td></tr>   
  10. <tr id="33333"><td>333333333333333333333333333333</td></tr>   
  11. <tr id="4444"><td>444444444444444444444444444444</td></tr>   
  12. <tr id="55555"><td>555555555555555555555555555555</td></tr>   
  13. </table>   
  14. </div>   
  15. <script language="javascript" type="text/javascript">   
  16. function eventfun(){//1.直接把js方法寫在頁面結構上 
  17. console.log(this);//這裡涉及到一個this作用域的問題,eventfun再這裡是一個全域性函式, 物件是window,this指向的是window 
  18. alert(this);   
  19. }   
  20. function eventfun2(eve){//在這裡把事件物件作為引數傳遞到全域性方法裡 
  21. eve.name="alex";// 
  22. window.name="robin";   
  23. console.log(this);//[object Window] 
  24. console.log(eve);// [object HTMLInputElement] 
  25. console.log(this.name);// robin 
  26. console.log(eve.name);// alex 
  27. var self=eve;   
  28. console.log(this.name);//robin 
  29. console.log(self.name);//alex 
  30. alert(window.name);   
  31. alert(self.name);   
  32. }   
  33. function eventfun3(){//1.直接把js方法寫在頁面結構上 
  34. console.log(this);//這裡涉及到一個this作用域的問題,eventfun再這裡是一個全域性