《javascript高級程序設計》讀書筆記(九)

最近一個多月真是忙到飛起啊,活脫脫把美少女變成了國寶,終於告一段落,留給個人,依然是無處安放的,魅力(黑眼圈)啊~
話很少說,先啃爲敬 ^_^javascript


第13章 事件

JavaScript與HTML之間的交互式經過事件實現的。
事件,就是文檔或瀏覽器窗口中發生的一些特定的交互瞬間。html

事件流—事件冒泡

IE的事件流叫作事件冒泡,即事件開始時由最具體的元素(文檔中嵌套層次最深的那個節點)接收,而後諸暨向上傳播到較爲不具體的節點(文檔)。
好比一個<body>標籤中只包含一個<div>元素的頁面,若是你點擊了頁面中的<div>,那麼這個click事件會按照以下順序傳播:
(1) <div>
(2) <body>
(3) <html>
(4) document
也就是說,click事件首先在<div>元素上發生,而這個元素就是咱們點擊的元素。
而後,click事件沿DOM樹向上傳播,在每一級節點上都會發生,直至傳播到document對象。
下圖完美的展現了時間冒泡的過程。
image.pngjava

事件流—事件捕獲

事件捕獲的思想是不太具體的節點應該更早接收到事件,而最具體的節點應該最後接收到事件。
事件捕獲的用意在於在事件到達預約目標以前捕獲它。以上面冒泡的例子,單擊<div>元素就會如下列順序觸發click事件
(1) document
(2) <html>
(3) <body>
(4) <div>
在事件捕獲過程當中,document對象首先接收到click事件,而後事件沿DOM樹依次向上,一直傳播到事件的實際目的,即<div>元素。以下圖:
image.pngios

事件流—DOM事件流

"DOM2級事件"規定的事件流包括三個階段:事件捕獲階段、處於目標階段和事件冒泡階段。
首先發生的是事件捕獲,爲截獲事件提供了機會。而後是實際的目標接收到事件。最後一個階段是冒泡階段,能夠在這個階段對事件作出響應。
image.png瀏覽器

事件處理程序

事件就是用戶或瀏覽器自身執行的某種動做。諸如click、load和mouseover,都是事件的名字。而響應某個事件的函數就叫作事件處理程序(或事件監聽器)。
事件處理程序的名字以"on"開頭,所以click事件的事件處理程序就是onclick,load事件的事件處理程序就是onload。爲事件指定處理程序的方式有好幾種。框架

事件處理程序——HTML事件處理程序

事件處理程序中的代碼在執行時,有權訪問全局做用域中的任何代碼。
這樣指定事件處理程序具備一些獨到之處。首先,這樣會建立一個封裝着元素屬性值的函數,這個函數中有一個局部變量event,也就是事件對象
<input type="button" value="Click Me" onclick="alert(event.type)"> //輸出"click"
經過event變量,能夠直接訪問事件對象,你不用本身定義它,也不用從函數的參數列表中讀取。在這個函數內部,this值等於事件的目標元素,例如:
<input type="button" value="Click Me" onclick="alert(event.type)"> //輸出 "Click Me"函數

事件處理程序——DOM0級事件處理程序

每一個元素(包括window和document)都有本身的時間處理程序屬性,這些屬性一般所有小寫,例如onclick,將這種屬性的值設置爲一個函數,就能夠指定事件處理程序。性能

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert(this.id);    //"myBtn"
}
btn.onclick = null;      //刪除事件處理程序
事件處理程序——DOM2級事件處理程序

"DOM2級事件"定義了兩個方法,用於處理指定和刪除事件處理程序的操做:addEventListener()和removeEventListener()。
全部DOM節點中都包含這兩個方法,而且它們都接受3個參數:要處理的事件名、做爲事件處理程序的函數和一個布爾值。
最後這個布爾值參數若是是true,表示在捕獲階段調用事件處理程序;若是是false,表示在冒泡階段調用事件處理程序。this

var btn = document.getElmentById("myBtn");
btn.addEventListener("click",function(){
    alert(this.id);
},false);   //該事件會在冒泡階段被觸發
btn.addEventListener("click",function(){
    alert("Hello world!");
},false);

使用DOM2級方法添加事件處理程序的主要好處是能夠添加多個事件處理程序。
上面的例子爲按鈕添加了兩個事件處理程序,這兩個事件處理程序會按照添加它們的順序觸發,所以首先會顯示元素的ID,其次會顯示"Hello world!"消息。
經過addEventListener()添加的事件處理程序只能使用removeEventListener()來移除;移除時傳入的參數與添加處理程序時使用的參數相同。這也意味着經過addEventListener()添加的匿名函數將沒法移除。就像上面的例子同樣。spa

事件處理程序——IE事件處理程序

IE實現了與DOM中相似的兩個方法:attachEvent()和detachEvent()。這兩個方法接受相同的兩個參數:事件處理程序名字與事件處理程序函數。
要使用attachEvent()爲按鈕添加一個事件處理程序,可使用如下代碼。

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

注意,attachEvent()的第一個參數是"onclick",而非DOM的addEventListener()方法中的"click"。

在IE中使用attachEvent()與使用DOM0級方法的主要區別在於事件處理程序的做用域。在使用DOM0級方法的狀況下,事件處理程序會在其所屬元素的做用域內運行;在使用attachEvent()方法的狀況下,事件處理程序會在全局做用域中運行,因襲this等於window。
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){

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

});
在編寫跨瀏覽器的代碼時,牢記這一區別很是重要。
與addEventListener()相似,attachEvent()方法也能夠用來爲一個元素添加多個事件處理程序。
與DOM方法不一樣的是,這些事件處理程序不是以添加它們的順序執行,而是以相反的順序被觸發。
使用attachEvent()添加的事件能夠經過detachEvent()來移除,條件是必須提供相同的參數。與DOM方法同樣,這也意味着添加的匿名函數將不能被移除。

事件對象——DOM中的事件對象

兼容DOM的瀏覽器會將一個event對象傳入到事件處理程序中。不管指定事件處理程序時使用什麼方法(DOM0級或DOM2級),都會傳入event對象。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert(event.type);       //"click"
};
btn.addEventListener("click", function(event) {
    alert(event.type);       //"click"
}, false);

event對象包含與建立它的特定事件有關的屬性和方法。觸發的事件類型不同,可用的屬性和方法也不同。全部的事件都會有下表列出的成員。
image.png
image.png

在事件處理程序內部,對象this始終等於currentTarget的值,而target則只包含事件的實際目標。
若是直接將事件處理程序制定給了目標元素,則this、currentTarget和target包含相同的值。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert(event.currentTarget === this);     //true
    alert(event.target === this);       //true
}

要阻止特定事件的默認行爲,可使用preventDefault()方法。
例如,連接的默認行爲就是在被單擊時會導航到其href特性指定的URL。若是你想阻止連接導航這一默認行爲,那麼經過連接的onclick事件處理程序能夠取消它。

var link = document.getElementById("myLink");
link.onclick = function(event) {
    event.preventDefault();
}

只有cancelable屬性設置爲true的事件,纔可使用preventDefault()來取消其默認行爲。
另外,stopPropagation()方法用於當即中止事件在DOM層次中的傳播,即取消進一步的事件捕獲或冒泡。
例如,直接添加到一個按鈕的時間處理程序能夠調用stopPropagation(),從而避免觸發註冊在document.body上面的事件處理程序。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert("Clicked");
    event.stopPropagation();
};
document.body.onclick = function(event) {
    alert("body clicked");
}

對於上面例子,若是不調用stopPropagation(),就會在單擊按鈕時出現兩個警告框,但是因爲click事件根本不會傳播到document.body,所以就不會觸發註冊在這個元素上的onclick事件處理程序。

事件對象的eventPhase屬性,能夠用來肯定事件當前正位於事件流的哪一個階段。
若是是在捕獲階段調用的事件處理,那麼eventPhase等於1;
若是事件處理程序處於目標對象上,則eventPhase等於2;
若是是在冒泡階段調用的事件處理程序,eventPhase等於3.
這裏要注意的是,儘管「處於目標」發生在冒泡階段,但eventPhase仍然一直等於2。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert(event.eventPhase);   //2
};

document.body.addEventListener("click", function(event){
    alert(event.eventPhase);   //1
}, true);

document.body.onclick = function(event) {
    alert(event.eventPhase);   //3
};

當單擊這個例子中的按鈕時,首先執行的事件處理程序是在捕獲階段觸發的添加在document.body中的那一個,結果會彈出一個警告框顯示錶示eventPhase的1。接着,會觸發在按鈕上註冊的事件處理程序,此時的eventPhase值爲2。最後一個被觸發的事件處理程序,是在冒泡階段執行的添加到document.body上的那一個,顯示eventPhase的值爲3。而當eventPhase等於2時,this.target和currentTarget始終都是相等的。
只有在事件處理程序執行期間,event對象纔會存在;一旦事件處理程序執行完成,event對象就會被銷燬。

事件類型

Web瀏覽器中可能發生的事件有不少類型。

  • UI事件,當用戶與頁面上的元素交互時觸發;
  • 焦點事件,當元素得到或失去焦點時觸發;
  • 鼠標事件,當使用鼠標滾輪時觸發;
  • 文本事件,當在文檔中輸入文本時觸發;
  • 鍵盤事件,當用戶經過鍵盤在頁面上執行操做時觸發;
  • 合成事件,當爲IME輸入字符時觸發;
  • 變更事件,當底層DOM結構發生變化時觸發。
UI事件

UI事件指的是那些不必定與用戶操做有關的事件。

  • load:當頁面徹底加載後在window上面觸發,當全部框架都加載完畢時在框架集上面觸發,當圖像加載完畢時在<img>元素上面觸發,或者當嵌入的內容加載完畢時在<ocject>元素上面觸發。
  • unload:當頁面徹底卸載後在window上面觸發,當全部框架都卸載後在框架上面觸發,或者當嵌入的內容卸載完畢後在<object>元素上面觸發。
  • error:當發生javascript錯誤時在window上面觸發,當沒法加載圖像時在<img>元素上面觸發,當沒法加載嵌入內容時在<object>元素上面觸發,或者當有一或多個框架沒法加載時在框架集上面觸發。
  • select:當窗口或框架的大小變化時在window或框架上面觸發。
  • resize:當窗口或框架的大小變化時在window或框架上面觸發。
  • scroll:當用戶滾動帶滾動條的元素中的內容時,在該元素上面觸發。<body>元素中包含所加載頁面的滾動條。
焦點事件

焦點事件會在頁面得到或失去焦點時觸發。利用這些事件並與document.hasFocus()方法及document.activeElement屬性配合,能夠知曉用戶在頁面上的行蹤。

  • blur:在元素失去焦點時觸發。這個事件不會冒泡,全部瀏覽器都支持它。
  • focus:在元素得到焦點時觸發。這個事件不會冒泡,全部瀏覽器都支持它。
鼠標與滾輪事件
  • click:在用戶單擊主鼠標按鈕或者按下回車鍵時觸發。
  • dblclick:在用戶雙擊主鼠標按鈕時觸發。
  • mousedown:在用戶按下了任意鼠標按鈕時觸發。不能經過鍵盤觸發這個事件。
  • mouseenter:在鼠標光標從元素外部首次移動到元素範圍以內時觸發。這個事件不冒泡,並且在光標移動到後代元素上不會觸發。
  • mouseleave:在位於元素上方的鼠標光標移動到元素範圍以外時觸發。這個事件不冒泡,並且在光標移動到後代元素上不會觸發。
  • mousemove:當鼠標指針在元素內部移動試重複地觸發。不能經過鍵盤觸發這個事件。
  • mouseout:在鼠標指針位於一個元素上方,而後用戶將其移入另個元素時觸發。又移入的另外一個元素可能位於前一個元素的外部,也多是這個元素的子元素。
  • mouseover:在鼠標指針位於一個元素外部,而後用戶將其首次移入另外一個元素邊界以內時觸發。
  • mouseup:在用戶釋放鼠標按鈕時觸發。

頁面上的全部元素都支持鼠標事件。
除了mouseenter和mouseleave,全部鼠標事件都會冒泡,,也能夠被取消,而取消鼠標事件將會影響瀏覽器的默認行爲。

只有在同一個元素上相繼觸發mousedown和mouseup事件,纔會觸發click事件;若是mousedown和mouseup中的一個被取消,就不會觸發click事件。
相似地,只有觸發兩次click事件,纔會觸發一次dblclick事件。
這四個事件觸發的順序始終以下:
(1)mousedown
(2)mouseup
(3)click
(4)mousedown
(5)mouseup
(6)click
(7)dblclick
顯然,click和dblclick事件都會依賴於其餘先行事件的觸發;而mousedown和mouseup則不受其餘事件的影響。

1.客戶區座標位置
鼠標事件都是在瀏覽器視口中的特定位置上發生的。這個位置信息保存在事件對象的clientX和clientY屬性中。
它們的值表示事件發生時鼠標指針在視口中的水平和垂直座標。
2.頁面座標位置
經過客戶區座標可以知道鼠標是在視口中什麼位置發生的,而頁面座標經過事件對象的pageX和pageY屬性,能告訴你事件是在頁面中的什麼位置發生的。換句話說,這兩個屬性表示鼠標光標在頁面中的位置,所以座標是從頁面自己而非視口的左邊和頂邊計算的。
在頁面沒有滾動的狀況下,pageX和pageY的值與clientX和clientY的值相等。
3.屏幕座標位置
鼠標事件發生時,不只會有相對於瀏覽器窗口的位置,還有一個相對於整個電腦屏幕的位置。而經過screenX和screenY屬性就能夠肯定鼠標事件發生時鼠標指針相對於整個屏幕的座標信息。
4.鼠標按鈕
只有在主鼠標按鈕被單擊時纔會觸發click事件,所以檢測按鈕的信息並非必要的。但對於mousedown和mouseup事件來講,則在其event對象存在一個button屬性,表示按下或釋放的按鈕。
DOM的button屬性可能有以下3個值:0表示主鼠標按鈕,1表示中間的鼠標按鈕(鼠標滾輪按鈕),2表示次鼠標按鈕。
4.觸摸設備
ios和Android設備的實現很是特別,由於這些設備沒有鼠標。

  • 不支持dblclick事件。雙擊瀏覽器窗口會放大畫面,並且沒有辦法改變該行爲。
  • 輕擊可單擊元素會觸發mousemove事件。若是此操做會致使內容變化,將再也不有其餘事件發生;若是屏幕沒有所以變化,那麼會依次發生mousedown、mouseup和click事件。輕擊不可單擊的元素不會觸發任何事件。
  • mousemove事件也會觸發mouseover和mouseout事件。
  • 兩個手指放在屏幕上且頁面隨手指移動而滾動時會觸發mousewheel和scroll事件。
鍵盤與文本事件

有3個鍵盤事件,簡述以下。

  • keydown:當用戶按下鍵盤上的任意鍵時觸發,並且若是按住不放的話,會重複觸發此事件。
  • keypress:當用戶按下鍵盤上的字符鍵時觸發,並且若是按住不放的話,會重複觸發此事件。
  • keyup:當用戶釋放鍵盤上的鍵時觸發。
觸摸與手勢事件-觸摸事件

touchstart:當手指觸摸屏幕時觸發;即便已經有一個手指放在了屏幕上也會觸發。
touchmove:檔收視在屏幕上滑動時連續地觸發。在這個事件發生期間,調用preventDefault()能夠阻止滾動。
touchend:當手指從屏幕上移開時觸發。
touchcancel:當系統中止跟蹤觸摸時觸發。
以上這幾個事件都會冒泡,也都會取消。

觸摸與手勢事件-手勢事件

ios2.0中的Safari還引入了一組手勢事件。當兩個手指觸摸屏幕時就會產生手勢,手勢一般會改變顯示項的大小,或者旋轉顯示項。

  • gesturestart:當一個手指已經按在屏幕上而另外一個手指又觸摸屏幕時觸發。
  • gesturechange:當觸摸屏幕的任何一個手指的位置發生變化時觸發。
  • gestureend:當任何一個手指從屏幕上面移開時觸發。

內存和性能——事件委託
對「事件處理程序過多」問題的解決方案就是事件委託。事件委託利用了事件冒泡,只指定一個事件處理程序,就能夠管理某一類型的全部事件。例如,click事件會一直冒泡到document層次。
若是可行的話,也能夠考慮爲document對象添加一個事件處理程序,用以處理頁面上發生的某種特定類型的事件。優勢以下:

  • document對象很快就能夠訪問,並且能夠在頁面生命週期的任什麼時候點上爲它添加事件處理程序(無須等待DOMContentLoaded或load事件)。換句話說,只要可單擊的元素呈如今頁面上,就能夠當即具有適當的功能。
  • 在頁面中設置事件處理程序所需的時間更少。只添加一個事件處理程序所需的DOM引用更少,所花的時間也更少。
  • 整個頁面佔用的內存空間更少,可以提高總體性能。

最適合採用事件委託技術的事件包括click、mousedown、mouseup、keydown、keyup和keypress。雖然mouseover和mouseout事件也冒泡,但要適當處理它們並不容易,並且常常須要計算元素的位置。(由於當鼠標從一個元素移到其子節點時,或者當鼠標移出該元素時,都會觸發mouseout事件。)

小結
事件是將Javascript與網頁聯繫在一塊兒的主要形式。「DOM3級事件」規範和HTML5定義了常見的大多數事件。即便有規範定義了基本事件,但不少瀏覽器仍然在規範以外實現了本身的專有事件,從而爲開發人員提供更多掌握用戶交互的手段。

  • 在使用事件時,須要考慮以下一些內存與性能方面的問題
  • 有必要限制一個頁面中事件處理程序的數量,數量太多會致使佔用大量內存,並且也會讓用戶感受頁面反應不夠靈敏。
  • 創建在事件冒泡機制之上的事件委託技術,能夠有效地減小事件處理程序的數量。
  • 創建在瀏覽器卸載頁面以前移除頁面中的全部事件處理程序。

事件是Javascript中最重要的主題之一,深刻理解事件的工做機制以及它們對性能的影響相當重要。

好啦,事件就大概記到這裏啦~

相關文章
相關標籤/搜索