[JavaScript] Event
前言
這篇筆記關於Javascript事件操作的觀念。
JavaScript 是一個事件驅動 (Event-driven) 的程式語言。
什麼是事件驅動?
就是當瀏覽器載入網頁,開始讀取文檔(document)後,雖然馬上會讀取 JS事件相關的程式碼,但需等到「事件」被觸發(滑鼠點擊、按下鍵盤)後,才會再執行相應程式。
Event Flow(事件流程)
一個事件發生後,會在子元素和父元素之間傳遞。傳遞分成三階段。我們可以觀察一下網頁元素接收事件的順序。
第一階段:從根結點window傳導到目標節點(上層傳到底層),稱為”捕獲階段”(capture phase)。
第二階段:在目標節點上觸發,稱為”目標階段”(target phase)。
第三階段:從目標節點傳導回window節點(從底層傳回上層),稱為”冒泡階段”(bubbling phase)。
addEventListener() 事件監聽
用於綁定事件的監聽函數,可以利用addEventListener()來進行DOM的事件操作, 用於在當前節點或對象上面。
.addEventListener(type, listener[, useCapture]);
這個方法中可傳入3個參數:
type:事件名稱,有區分大小寫。
listener:監聽函數。事件發生時,會呼叫該監聽函數。
useCapture:Boolean值,true: 事件捕獲 ; false: 事件冒泡。
Event Propagation(事件冒泡)
當一個元素接收到事件後,會將接收到的事件往上傳給父元素,一直傳到頂端的根節點(document)。
來看個例子:
今天有3個不同顏色及大小的方格分別為紅(view1)、藍(view2)、紫(view3),點擊view3(被包在最裡面)時,view2和view1和document也會同時被觸發。
示意圖:
觀察觸發的順序,可以發現由觸發事件節點由內而外,來比較一下相對於它的「事件捕獲」。
Event Capturing(事件捕獲)
相對於「事件冒泡」,「事件捕獲」是從最外層,一層一層內方向傳遞事件,和「事件冒泡」相反。再使用一次剛剛的圖作範例XD
實作的話可以用下方的程式碼:
1 |
|
點擊view3獲得的結果會是
事件傳遞流程
事件一開始從文檔的根節點流向目標對象(捕獲階段),然後在目標對向上被觸發(目標階段),之後再回到文檔的根節點。
如果今天不想要將事件傳遞到所有的父元素節點,那要如何阻止事件冒泡呢? 程式碼如下:
非IE: stopPropagation() ; IE: ev.cancelBubble = true;
兼容寫法: ev.stopPropagation ? ev.stopPropagation() : ev.cancelBubble = true;
我們可以在監聽事件的函數傳入一個ev做參數,作為事件來源。回到剛才例子,假設只想要讓事件從頂層傳遞到view2的話,可以在view2的事件監聽處打上上面那段程式碼。範例如下:
1 |
|
所得結果
事件捕獲與冒泡都同樣適用。
preventDefault 取消預設行為
可以取消HTML元素的預設行為 ,如 <a>
連結,或表單submit 等等, 如果我們需要在這些元素上綁定事件,但又不想要觸發它的預設行為,那取消它們的預設行為就是很重要的一件事。範例:有一個通往 yahoo連結 <a>
:
1 |
|
結果會發現,即便我們為了<a>
去註冊了 click 事件,但是當我點擊這個 link 的時候,瀏覽器依然會把我帶去 yahoo的網頁。
如果這時候,我希望執行的是 console.log('yahoo!')
; 而不是直接把我帶去 yahoo的網站,那麼可以怎麼做?
這時候可以利用 event 物件提供的 event.preventDefault() :
1 |
|
再點擊 link 一次,你會發現瀏覽器預設的跳轉頁面的行為不見了, console.log('yahoo!');
也可順利執行。
JavaScript 的 addEventListener() 裡,最後面加上 return false 也會有同樣的效果。
在 jQuery 的addEventListener() 裡最後加上 return false 來得到preventDefault() 與 stopPropagation() 的效果是沒問題的。
事件(event)對象:
當一個事件發生時,和當前對象發生的相關信息,都會臨時保存在這個對象中
事件源:
觸發該事件的來源節點,以下為兼容不同瀏覽器的寫法:
IE: srcElement屬性
firefox target屬性
google/safari: srcElement、target都有
例子:
1 |
|
使用keydown事件來觸發,我們可以傳入ev這個參數做事件源,根據使用者在鍵盤上輸入的鍵所輸出的數值,來確定使用者按下哪個鍵,例如輸入enter,輸出的數值就是13,tab鍵則是9。
舉個應用:
1 |
|
點擊上面的紫色方形可以取得滑鼠相對於瀏覽器、事件源的位置。
元素節點的事件屬性
除addEventListener()方法外,元素節點的事件屬性,同樣可以指定監聽函數。
1 |
|
“元素節點的事件屬性”的缺點:
同一個事件只能定義一個監聽函數。如果定義兩次onclick屬性,後一次定義會覆蓋前一次。
使用這個方法指定的監聽函數,也是只會在冒泡階段觸發
EventTarget.addEventListener是比較推薦的指定監聽函數的方法,優點:
同一個事件可以添加多個監聽函數。
能夠指定在哪個階段(捕獲階段還是冒泡階段)觸發監聽函數。
this 的指向
若是想要對「觸發事件的元素」做某些事時,可以使用this方法來達成。
1 |
|