最近一段時間正在對JavaScript進行學習,知識太多,需要進行實際的使用和總結,國慶長假正好有時間,寫了下麵對JavaScript總結,可能對事件的理解還不夠完善,希望讀者多多指導,拍磚,我將不勝感激。好了直接如題吧。 JavaScript中的事件流 DOM2級事件規定事件流分為3個階段: 第一 ...
最近一段時間正在對JavaScript進行學習,知識太多,需要進行實際的使用和總結,國慶長假正好有時間,寫了下麵對JavaScript總結,可能對事件的理解還不夠完善,希望讀者多多指導,拍磚,我將不勝感激。好了直接如題吧。
JavaScript中的事件流
DOM2級事件規定事件流分為3個階段:
第一階段:事件捕獲階段,先由文檔的根節點Window->Document->html->body…往事件觸發對象,從外向內捕獲事件對象。
第二階段:目標階段,到達目標事件位置,觸發事件;
第三階段:事件冒泡,再從目標事件位置往文檔的根節點方向,反向冒泡。
事件捕獲和事件冒泡的先後順序如上圖顯示的步驟(1)、(2)、(3)。
在此簡單的做一下上面的捕獲和冒泡的實驗:
環境就以常用的Chrome瀏覽器,代碼如下:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>Event</title> 5 </head> 6 <body> 7 <div id="btn-big-test"> div1 8 <br/> 9 <br/> 10 <div id="btn-big-test2">div2</div> 11 </div> 12 </body> 13 <script type="text/javascript"> 14 var el = document.getElementById('btn-big-test'); 15 var el2 = document.getElementById('btn-big-test2'); 16 el.addEventListener('click', function(event) { 17 console.log("冒泡1>>" + Date.parse(new Date())); 18 }, false); 19 20 el.addEventListener('click', function() { 21 console.log("捕獲1>>" + Date.parse(new Date())); 22 }, true); 23 el2.addEventListener('click', function() { 24 console.log("冒泡2>>" + Date.parse(new Date())); 25 }, false); 26 27 el2.addEventListener('click', function() { 28 console.log("捕獲2>>" + Date.parse(new Date())); 29 }, true);View Code
運行結果如下如下:
看來結果卻不是我們想象的那樣 先執行捕獲在執行冒泡,那麼問題來了,是什麼問題。想了下不如把下麵兩個註冊事件換一下順序:
1 el2.addEventListener('click', function() { 2 console.log("捕獲2>>" + Date.parse(new Date())); 3 }, true); 4 5 el2.addEventListener('click', function() { 6 console.log("冒泡2>>" + Date.parse(new Date())); 7 }, false);View Code
運行結果如下:
現在好了,運行的結果和想象的一樣,那麼之前怎麼會不一樣的呢?順便把這幾個全部的順序都換一下,發現無論怎麼換 “捕獲1”都會先列印出來,但是事件觸發的元素的捕獲和冒泡的事件會因為順序不一樣而不同。
由此可見:事件流確實是先由外向內先捕獲,在向外冒泡,觸發的元素這不同,觸發元素是事件的目標元素,那麼它的捕獲和觸發呢,那就是根據事件註冊的先後順序不同來執行的。
說完了事件流的流程,再來說下事件的觸發吧。
事件是用戶或瀏覽器自身執行的某種動作。如click、load和mouseover,都是事件的名字,而響應某個事件執行的函數就是事件處理程式,那麼怎麼進行事件綁定了,其實上面已經有了,
(1)、執行 addEventListener(‘click’,function(){}, false),就表示註冊了一個click事件。
(2)、在html 標簽中使用 onclick=”yourfunction()” 這個是最常見的方式。
如果你發現addEventListener在ie裡面註冊不了事件,這是因為IE不同需要採用attachEvent ('onclick', function(){}) 來綁定事件
(3)事件移除則使用removeEventListener或detachEvent。
在此我們已經知道瞭如何給元素綁定事件,那麼觸發事件對象中包含哪些內容呢?
事件類型
UI事件、焦點事件、滑鼠事件、滾輪事件、文本事件、鍵盤事件、合成事件、變動事件、變動名稱事件。暫時不做詳細總結,下次待完善…
記憶體和性能
在JS的世界里,每個函數都是對象,都會占用記憶體;記憶體中的對象越多,性能就越差。其次,必須事先指定所有事件處理程式而導致的DOM訪問次數,會延遲整個頁面的交互就緒時間。事實上,從如何利用好事件程式的角度出發,還是有一些方法能夠提升性能的。
對“事件處理程式過多”問題的解決方案就是事件委托。事件委托利用了事件冒泡,只指定一個事件處理程式,就可以管理某一類型的所有事件。例如,click事件會一直冒泡到Document層次。也就是說我們可以為整個頁面指定一個onclick事件處理程式,不必每個元素添加處理事件。多個元素點擊的事件合併根據event.target.id來確定處理邏輯,這樣雖然代碼處理函數變長,但是消耗會更低。如果可行是否可以在document對象添加一個事件處理程式,用於處理頁面的某種特定類型的事件,
(1)、Doucument對象很快就可以訪問,而且可以在頁面生命周期的任何時間點上為它添加程式(無須等待DOMContentLoaded或load事件)。也就是說,只要可點單擊的元素呈現在頁面上,就可以立即具備適當的功能。
(2)、在頁面中設置事件處理程式所需的時間更少。只添加一個事件處理程式所需的DOM應用更少,所花的時間也更少。
(3)、整個頁面占用的記憶體空間更少,能夠提升整體性能。
但是也要避免綁定的函數對象過大,適可而止。
事件模擬
採用js在任意時刻來觸發特定的事件,如同瀏覽器創建的事件一樣。
DOM中的事件模擬分為UIEvents、MouseEvents、MutationEvents、HTMLEvents,暫時不做詳細總結,下次待完善…
好了今天事件總結的最後一個內容了,自定義事件
事件是一種觀察者的設計模式,一種鬆散耦合代碼的技術。對象可以發佈事件,用來表示該對象生命周期中某個時刻到了。然後其他對象可以觀察該對象,等待這些時刻到來並通過運行代碼來響應。
觀察者由兩類對象組成:主體和觀察者,主體負責發佈事件,同時觀察者通過訂閱這些事件來觀察該主體。事件處理代碼便是觀察者 DOM元素則是主體,通過註冊事件進行註冊回調函數(事件處理程式)。
當代碼中存在多個部分在特定時刻相互交互的情況下,自定義事件就很有用。這時,如果每個對象都有對所有對象的引用,那麼整個代碼就會緊耦合,維護困難,因為對某個對象的修改也會影響到其他對象。使用自定義事件有助於解耦相關對象,保持功能的隔絕。很多情況中,觸發事件的代碼和監聽事件的代碼是完全分離的。
下麵給出一個代碼例子
1 function EventTarget(){ 2 this.handlers={}; 3 } 4 5 EventTarget.prototype={ 6 constructor:EventTarget, 7 addHandler:function(type,handler){ 8 if(typeof this.handlers[type] == "undefined"){ 9 this.handlers[type]=[]; 10 } 11 this.handlers[type].push(handler); 12 }, 13 fire:function(event){ 14 if(!event.target){ 15 event.target=this; 16 } 17 if(this.handlers[event.type] instanceof Array){ 18 var handlers=this.handlers[event.type]; 19 for(var i=0,len=handlers.length;i<len;i++){ 20 handlers[i][event]; 21 } 22 } 23 24 }, 25 removeHandler:function(type,handler){ 26 if(this.handlers[type] instanceof Array){ 27 var handlers=this.handlers[type]; 28 for(var i=0,len=handlers.length;i<len;i++){ 29 if(handlers[i]=== handler){ 30 break; 31 } 32 } 33 handlers.splice(i,1); 34 35 } 36 } 37 };View Code
1 使用EventTarget類型的自定義事件可以如下使用: 2 function handleMessage(event){ 3 alert("Message received:"+event.message); 4 } 5 //創建一個新對象 6 var target = new EventTarget(); 7 //添加一個事件處理程式 8 target.addHandler("message",handleMessage); 9 //觸發事件 10 target.fire({type:"message",message:"Hello World"}); 11 //刪除事件處理程式 12 target.removeHandler("message",handleMessage); 13 //再次,應沒有處理程式 14 target.fire({type:"message",message:"hello world"});View Code
到此我所理解的事件基本上算是總結完啦,有些部分還沒有總結出來,後面有時間再做詳細的總結學習。
參考資料:
(1)、《JavaScript高級程式設計》第3版
(2)、w3c:https://www.w3.org/TR/DOM-Level-3-Events/#ui-events-intro