天天看點

《JavaScript進階程式設計》讀書筆記(七):事件1.事件流2.事件處理程式3.事件對象4.事件類型5.模拟事件6.總結

1.事件流

當浏覽器發展到第四代時(IE4 及 Netscape Communicator 4),浏覽器開發團隊遇到了一個很有意思的問題:頁面的哪一部分會擁有某個特定的事件?要明白這個問題問的是什麼,可以想象畫在一張紙上的一組同心圓。如果你把手指放在圓心上,那麼你的手指指向的不是一個圓,而是紙上的所有圓。兩家公司的浏覽器開發團隊在看待浏覽器事件方面還是一緻的。如果你單擊了某個按鈕,他們都認為單擊事件不僅僅發生在按鈕上。換句話說,在單擊按鈕的同時,你也單擊了按鈕的容器元素,甚至也單擊了整個頁面。

事件流描述的是從頁面中接收事件的順序。但有意思的是,IE 和 Netscape 開發團隊居然提出了差不多是完全相反的事件流的概念。IE 的事件流是事件冒泡流,而 Netscape Communicator 的事件流是事件捕獲流。 

1.事件冒泡

IE的事件流叫做事件冒泡(event bubbling),即事件開始時由最具體的元素(文檔中嵌套層次最深的那個節點)接收,然後逐級向上傳播到較為不具體的節點(文檔)。 

<!DOCTYPE html>
    <html>
    <head>
      
<title>Event Bubbling Example</title>
    </head>
      
<body>
        <div id="myDiv">Click Me</div>
      
</body>
    </html>
      

以這個為例:

《JavaScript進階程式設計》讀書筆記(七):事件1.事件流2.事件處理程式3.事件對象4.事件類型5.模拟事件6.總結

2.事件捕獲

Netscape Communicator 團隊提出的另一種事件流叫做事件捕獲(event capturing)。事件捕獲的思想是不太具體的節點應該更早接收到事件,而最具體的節點應該最後接收到事件。事件捕獲的用意在于在事件到達預定目标之前捕獲它。 

《JavaScript進階程式設計》讀書筆記(七):事件1.事件流2.事件處理程式3.事件對象4.事件類型5.模拟事件6.總結

雖然事件捕獲是 Netscape Communicator 唯一支援的事件流模型,但 IE9、Safari、Chrome、Opera和 Firefox 目前也都支援這種事件流模型。盡管“DOM2 級事件”規範要求事件應該從 document 對象開始傳播,但這些浏覽器都是從 window 對象開始捕獲事件的。由于老版本的浏覽器不支援,是以很少有人使用事件捕獲。我們也建議讀者放心地使用事件冒泡,在有特殊需要時再使用事件捕獲。

3.DOM事件流

“DOM2級事件”規定的事件流包括三個階段:事件捕獲階段、處于目标階段和事件冒泡階段。首先發生的是事件捕獲,為截獲事件提供了機會。然後是實際的目标接收到事件。最後一個階段是冒泡階段,可以在這個階段對事件做出響應。以前面簡單的 HTML 頁面為例,單擊<div>元素會按照圖13-3所示順序觸發事件。 

《JavaScript進階程式設計》讀書筆記(七):事件1.事件流2.事件處理程式3.事件對象4.事件類型5.模拟事件6.總結

在 DOM 事件流中,實際的目标(<div>元素)在捕獲階段不會接收到事件。這意味着在捕獲階段, 12事件從 document 到<html>再到<body>後就停止了。下一個階段是“處于目标”階段,于是事件在<div>上發生,并在事件處理(後面将會讨論這個概念)中被看成冒泡階段的一部分。然後,冒泡階段發生,事件又傳播回文檔。 

多數支援 DOM 事件流的浏覽器都實作了一種特定的行為;即使“DOM2 級事件”規範明确要求捕獲階段不會涉及事件目标,但 IE9、Safari、Chrome、Firefox 和 Opera 9.5 及更高版本都會在捕獲階段觸發事件對象上的事件。結果,就是有兩個機會在目标對象上面操作事件。 

2.事件處理程式

onClick的方式會在事件流的冒泡階段被處理

用于處理指定和删除事件處理程式的操作:addEventListener()和 removeEventListener()。所有 DOM 節點中都包含這兩個方法,并且它們都接受 3 個參數:要處理的事件名、作為事件處理程式的函數和一個布爾值。最後這個布爾值參數如果是 true,表示在捕獲階段調用事件處理程式;如果是 false,表示在冒泡階段調用事件處理程式。 

通過 addEventListener()添加的事件處理程式隻能使用 removeEventListener()來移除;移除時傳入的參數與添加處理程式時使用的參數相同。這也意味着通過 addEventListener()添加的匿名函數将無法移除,如下面的例子所示。

var btn = document.getElementById("myBtn");
    btn.addEventListener("click", function(){
      
alert(this.id);
      

}, false);

//這裡省略了其他代碼

btn.removeEventListener("click", function(){ //沒有用!

alert(this.id);

}, false); 

大多數情況下,都是将事件處理程式添加到事件流的冒泡階段,這樣可以最大限度地相容各種浏覽器。最好隻在需要在事件到達目标之前截獲它的時候将事件處理程式添加到捕獲階段。如果不是特别需要,我們不建議在事件捕獲階段注冊事件處理程式。 

在 IE 中使用 attachEvent()與使用 DOM0 級方法的主要差別在于事件處理程式的作用域。在使用 DOM0 級方法的情況下,事件處理程式會在其所屬元素的作用域内運作;在使用 attachEvent()方法的情況下,事件處理程式會在全局作用域中運作,是以 this 等于 window。來看下面的例子。

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
      

alert(this === window); //true

});

在編寫跨浏覽器的代碼時,牢記這一差別非常重要。      

3.事件對象

在觸發 DOM 上的某個事件時,會産生一個事件對象 event,這個對象中包含着所有與事件有關的 3資訊。包括導緻事件的元素、事件的類型以及其他與特定事件相關的資訊。例如,滑鼠操作導緻的事件對象中,會包含滑鼠位置的資訊,而鍵盤操作導緻的事件對象中,會包含與按下的鍵有關的資訊。 

4.事件類型

  •   UI(User Interface,使用者界面)事件,當使用者與頁面上的元素互動時觸發;
  •   焦點事件,當元素獲得或失去焦點時觸發;
  •   滑鼠事件,當使用者通過滑鼠在頁面上執行操作時觸發;
  •   滾輪事件,當使用滑鼠滾輪(或類似裝置)時觸發;
  •   文本事件,當在文檔中輸入文本時觸發;
  •   鍵盤事件,當使用者通過鍵盤在頁面上執行操作時觸發;
  •   合成事件,當為 IME(Input Method Editor,輸入法編輯器)輸入字元時觸發;
  •   變動(mutation)事件,當底層 DOM 結構發生變化時觸發。 

1.UI事件

UI 事件指的是那些不一定與使用者操作有關的事件。 

  •   load:當頁面完全加載後在 window 上面觸發,當所有架構都加載完畢時在架構集上面觸發,當圖像加載完畢時在<img>元素上面觸發,或者當嵌入的内容加載完畢時在<object>元素上面觸發。
  •   unload:當頁面完全解除安裝後在 window 上面觸發,當所有架構都解除安裝後在架構集上面觸發,或者當嵌入的内容解除安裝完畢後在<object>元素上面觸發。
  •   abort:在使用者停止下載下傳過程時,如果嵌入的内容沒有加載完,則在<object>元素上面觸發。
  •   error:當發生 JavaScript 錯誤時在 window 上面觸發,當無法加載圖像時在<img>元素上面觸發,當無法加載嵌入内容時在<object>元素上面觸發,或者當有一或多個架構無法加載時在框

    架集上面觸發。第 17 章将繼續讨論這個事件。

  •   select:當使用者選擇文本框(<input>或<texterea>)中的一或多個字元時觸發。
  •   resize:當視窗或架構的大小變化時在 window 或架構上面觸發。 
  •   scroll:當使用者滾動帶滾動條的元素中的内容時,在該元素上面觸發。<body>元素中包含所加載頁面的滾動條。

2.焦點事件

 blur:在元素失去焦點時觸發。這個事件不會冒泡;所有浏覽器都支援它。

 DOMFocusIn:在元素獲得焦點時觸發。這個事件與 HTML 事件 focus 等價,但它冒泡。隻有Opera 支援這個事件。DOM3 級事件廢棄了 DOMFocusIn,選擇了 focusin。

 DOMFocusOut:在元素失去焦點時觸發。這個事件是 HTML 事件 blur 的通用版本。隻有 Opera支援這個事件。DOM3 級事件廢棄了 DOMFocusOut,選擇了 focusout。 

 focus:在元素獲得焦點時觸發。這個事件不會冒泡;所有浏覽器都支援它。

 focusin:在元素獲得焦點時觸發。這個事件與 HTML 事件 focus 等價,但它冒泡。支援這個事件的浏覽器有 IE5.5+、Safari 5.1+、Opera 11.5+和 Chrome。

 focusout:在元素失去焦點時觸發。這個事件是 HTML 事件 blur 的通用版本。支援這個事件的浏覽器有 IE5.5+、Safari 5.1+、Opera 11.5+和 Chrome。

當焦點從頁面中的一個元素移動到另一個元素,會依次觸發下列事件:

(1) focusout 在失去焦點的元素上觸發;

(2) focusin 在獲得焦點的元素上觸發;

(3) blur 在失去焦點的元素上觸發;

(4) DOMFocusOut 在失去焦點的元素上觸發;

(5) focus 在獲得焦點的元素上觸發;

(6) DOMFocusIn 在獲得焦點的元素上觸發。 

3.滑鼠與滾輪事件

 click:在使用者單擊主滑鼠按鈕(一般是左邊的按鈕)或者按下Enter鍵時觸發。這一點對確定易通路性很重要,意味着 onclick 事件處理程式既可以通過鍵盤也可以通過滑鼠執行。

 dblclick:在使用者輕按兩下主滑鼠按鈕(一般是左邊的按鈕)時觸發。從技術上說,這個事件并不是 DOM2 級事件規範中規定的,但鑒于它得到了廣泛支援,是以 DOM3 級事件将其納入了标準。

 mousedown:在使用者按下了任意滑鼠按鈕時觸發。不能通過鍵盤觸發這個事件。

 mouseenter:在滑鼠光标從元素外部首次移動到元素範圍之内時觸發。這個事件不冒泡,而且在光标移動到後代元素上不會觸發。DOM2 級事件并沒有定義這個事件,但 DOM3 級事件将它

納入了規範。IE、Firefox 9+和 Opera 支援這個事件。

 mouseleave:在位于元素上方的滑鼠光标移動到元素範圍之外時觸發。這個事件不冒泡,而且

在光标移動到後代元素上不會觸發。DOM2 級事件并沒有定義這個事件,但 DOM3 級事件将它

納入了規範。IE、Firefox 9+和 Opera 支援這個事件。

 mousemove:當滑鼠指針在元素内部移動時重複地觸發。不能通過鍵盤觸發這個事件。 

 mouseout:在滑鼠指針位于一個元素上方,然後使用者将其移入另一個元素時觸發。又移入的另一個元素可能位于前一個元素的外部,也可能是這個元素的子元素。不能通過鍵盤觸發這個事件。

 mouseover:在滑鼠指針位于一個元素外部,然後使用者将其首次移入另一個元素邊界之内時觸發。不能通過鍵盤觸發這個事件。

 mouseup:在使用者釋放滑鼠按鈕時觸發。不能通過鍵盤觸發這個事件。 

隻有在同一個元素上相繼觸發 mousedown 和 mouseup 事件,才會觸發 click 事件;如果mousedown 或 mouseup 中的一個被取消,就不會觸發 click 事件。類似地,隻有觸發兩次 click 事件,才會觸發一次 dblclick 事件。如果有代碼阻止了連續兩次觸發 click 事件(可能是直接取消 click事件,也可能通過取消 mousedown 或 mouseup 間接實作),那麼就不會觸發 dblclick 事件了。這 4個事件觸發的順序始終如下:

(1) mousedown

(2) mouseup

(3) click

(4) mousedown

(5) mouseup

(6) click

(7) dblclick

5.模拟事件

可以使用 JavaScript 在任意時刻來觸發特定的事件,而此時的事件就如同浏覽器建立的事件一樣。也就是說,這些事件該冒泡還會冒泡,而且照樣能夠導緻浏覽器執行已經指定的處理它們的事件處理程式。在測試 Web 應用程式,模拟觸發事件是一種極其有用的技術。 

可以在 document 對象上使用 createEvent()方法建立 event 對象。這個方法接收一個參數,即

表示要建立的事件類型的字元串。在 DOM2 級中,所有這些字元串都使用英文複數形式,而在 DOM3 級中都變成了單數。這個字元串可以是下列幾字元串之一。

  •   UIEvents:一般化的 UI 事件。滑鼠事件和鍵盤事件都繼承自 UI 事件。DOM3 級中是 UIEvent。
  •   MouseEvents:一般化的滑鼠事件。DOM3 級中是 MouseEvent。
  •   MutationEvents:一般化的 DOM 變動事件。DOM3 級中是 MutationEvent。
  •   HTMLEvents:一般化的 HTML 事件。沒有對應的 DOM3 級事件(HTML 事件被分散到其他類别中)。
例子:      
var btn = document.getElementById("myBtn");
      

//建立事件對象

var event = document.createEvent("MouseEvents");

//初始化事件對象

event.initMouseEvent("click", true, true, document.defaultView, 0, 0, 0, 0, 0,false, false, false, false, 0, null);

btn.dispatchEvent(event);      

6.總結

在使用事件時,需要考慮如下一些記憶體與性能方面的問題。

 有必要限制一個頁面中事件處理程式的數量,數量太多會導緻占用大量記憶體,而且也會讓使用者感覺頁面反應不夠靈敏。

 建立在事件冒泡機制之上的事件委托技術,可以有效地減少事件處理程式的數量。 建議在浏覽器解除安裝頁面之前移除頁面中的所有事件處理程式。

可以使用 JavaScript 在浏覽器中模拟事件。“DOM2 級事件”和“DOM3 級事件”規範規定了模拟事件的方法,為模拟各種有定義的事件提供了友善。此外,通過組合使用一些技術,還可以在某種程度上模拟鍵盤事件。IE8 及之前版本同樣支援事件模拟,隻不過模拟的過程有些差異。 

繼續閱讀