JavaScript DOM Event (事件處理)
使用者在瀏覽網頁時會觸發很多事件 (events) 的發生,例如按下滑鼠是一種事件、按下鍵盤按鍵是一種事件、網頁或圖片完成下載時是一種事件、表單欄位值被改變是一種事件..等等。
DOM Event 定義很多種事件型態,讓你可以用 JavaScript 來監聽 (listen) 和處理 (event handling) 這些事件。
事件名稱 | 觸發條件 |
---|---|
blur | 物件失去焦點時 |
change | 物件內容改變時 |
click | 滑鼠點擊物件時 |
dblclick | 滑鼠連點二下物件時 |
error | 當圖片或文件下載產生錯誤時 |
focus | 當物件被點擊或取得焦點時 |
keydown | 按下鍵盤按鍵時 |
keypress | 按下並放開鍵盤按鍵後 |
keyup | 按下並放開鍵盤按鍵時 |
load | 網頁或圖片完成下載時 |
mousedown | 按下滑鼠按鍵時 |
mousemove | 介於over跟out間的滑鼠移動行為 |
mouseout | 滑鼠離開某物件四周時 |
mouseover | 滑鼠進入一個元素 (包含進入該元素中的子元素) 四周時 |
mouseup | 放開滑鼠按鍵時 |
resize | 當視窗或框架大小被改變時 |
scroll | 當捲軸被拉動時 |
select | 當文字被選取時 |
submit | 當按下送出按紐時 |
beforeunload | 當使用者關閉 (或離開) 網頁之前 |
unload | 當使用者關閉 (或離開) 網頁之後 |
接下來我們來介紹怎麼針對特定事件來綁定事件處理函式 (event handler),分別有幾種做法。
DOM Level 0 - HTML Inline Attribute
你可以在 HTML 的事件相關屬性上綁定事件處理函式,屬性的名稱是「on + 事件名稱」,屬性值則是任何的 JavaScript。
範例:
<html>
<head>
<title>inline event handling example</title>
</head>
<body>
<button onclick="triggerAlert();">click me</button>
<script>
function triggerAlert() {
alert('Hey');
}
</script>
</body>
</html>
上面的例子中,當使用者點下按鈕會跳出 "Hey"。
this
你可以在 inline 的 function 傳入 this
當參數,表示 DOM 元素物件本身:
<html>
<head>
<title>inline event handling example</title>
</head>
<body>
<button onclick="triggerAlert(this);" data-name="Mike">click me</button>
<script>
function triggerAlert(ele) {
alert('Hey ' + ele.getAttribute('data-name'));
}
</script>
</body>
</html>
上面的例子中,當使用者點下按鈕會跳出 "Hey Mike"。
DOM Level 0 - DOM Object Property
DOM 元素 API 也有對應的屬性,可以用來綁定事件處理函式。
上面的例子可以改寫成:
<html>
<head>
<title>traditional event handling example</title>
</head>
<body>
<button id="foo">click me</button>
<script>
function triggerAlert() {
alert('Hey');
}
var ele = document.getElementById('foo');
// 當使用者按下 (click) 按鈕時,執行 triggerAlert 函數
ele.onclick = triggerAlert;
</script>
</body>
</html>
如果要取消綁定事件處理函數,將事件屬性值設為 null 就可以了:
ele.onclick = null;
DOM Level 2 - Element.addEventListener(eventType, listener)
addEventListener 方法可以用來綁定元素的事件處理函數,第一個參數 eventType 是事件名稱,第二個參數 listener 是事件處理函數。
例子:
<html>
<head>
<title>addEventListener example</title>
</head>
<body>
<script>
function myAlert() {
alert('Hey!');
}
// 當使用者用滑鼠點擊頁面時,執行 myAlert 函數
document.addEventListener('click', myAlert);
// 當網頁載入時,執行這個 callback 函數
window.addEventListener('load', function() {
alert('頁面已載入!');
});
</script>
</body>
</html>
addEventListener 相較於 DOM Level 0 的方法是它可以對一個元素同時指定多個事件處理函數:
<html>
<head>
<title>addEventListener example</title>
</head>
<body>
<script>
function myAlert1() {
alert('Hey!');
}
function myAlert2() {
alert('Hello!');
}
// 當使用者用滑鼠點擊頁面時,執行 myAlert1 函數
document.addEventListener('click', myAlert1);
// 當使用者用滑鼠點擊頁面時,執行 myAlert2 函數
document.addEventListener('click', myAlert2);
</script>
</body>
</html>
在上面的例子中點擊頁面依序會跳出 "Hey!" "Hello!"。
但要特別注意的是依不同瀏覽器的實作,執行順序可能不會等於事件處理函數指定的順序。
DOM Level 2 - Element.removeEventListener(eventType, listener)
removeEventListener 原來取消透過 addEventListener 綁定的事件處理函數,第一個參數 eventType 是事件名稱,第二個參數 listener 是先前綁定的事件處理函數。
用法:
<html>
<head>
<title> removeEventListener example</title>
</head>
<body>
<button id="foo">click me</button>
<script>
function myAlert() {
alert('Hey!');
}
var ele = document.getElementById('foo');
// 綁定 click 事件處理函數
ele.addEventListener('click', myAlert);
// 移除剛綁定的 click 事件處理函數
ele.removeEventListener('click', myAlert);
</script>
</body>
</html>
Legacy IE - attachEvent(eventType, listener), detachEvent(eventType, listener)
在 IE9 以下可以用 attachEvent 和 detachEvent 這兩個 IE 專有 (proprietary) 的方法來取代 addEventListener 和 removeEventListener。
注意用這兩個方法時的事件名稱前面要加上 "on",用法:
<html>
<head>
<title>IE example</title>
</head>
<body>
<button id="foo">click me</button>
<script>
function myAlert() {
alert('Hey!');
}
var ele = document.getElementById('foo');
// 綁定 click 事件處理函數
ele.attachEvent('click', myAlert);
// 移除剛綁定的 click 事件處理函數
ele.detachEvent('click', myAlert);
</script>
</body>
</html>
Event Bubbling (事件氣泡) VS Event Capturing (事件捕捉)
DOM 中的事件有傳播 (event flow) 的概念,當 DOM 事件發生時,事件會先由外到內 (capturing phase)、再由內到外 (bubbling phase) 的順序來傳播。
什麼意思?例如我們看這一個 HTML DOM 結構:
<html>
<head>
<title>event flow example</title>
</head>
<body>
<div>
<ul>
<li></li>
</ul>
</div>
</body>
</html>
當使用者點擊 li 元素時,事件觸發的順序是:
- Capturing 捕捉階段:
document -> <html> -> <body> -> <div> -> <ul> -> <li>
- Bubbling 氣泡階段:
<li> -> <ul> -> <div> -> <body> -> <html> -> document
DOM 中的元素會按照上面的順序依序地觸發其 click 事件。
在 addEventListener 和 removeEventListener 方法中,有第三個參數布林值 useCapture 用來指定事件處理函數是要在 Capturing 階段或 Bubbling 階段被執行,false (預設) 表示 Bubbling,true 表示 Capturing。
Event Object
當事件處理函數被執行時,會傳入一個參數代表 event object。
例如:
element.onclick = function(event) {
// ...
};
// 或是
element.addEventListener('click', function(event) {
// ...
});
在 IE9 以下的 DOM Level 0 或 attachEvent 方法不會傳入 event object,但有一個 global 的 window.event
object 可以用來代替,所以跨瀏覽器 (cross-browser) 的寫法可以改成:
element.onclick = function(event) {
event = event || window.event;
// ...
};
Event Object 有幾個常用的屬性 (property):
屬性 | 說明 |
---|---|
type | 返回事件類型,例如 "click" |
target | 指向觸發事件的 DOM element |
currentTarget | 在 event bubbling 階段中,指向目前執行的事件處理函數是綁定在哪個 DOM element 上 |
timeStamp | 事件發生時的時間 timestamp (單位是 milliseconds 毫秒) |
eventPhase | 返回為一個數字,表示事件處於目前所處的傳播狀態 (event flow),有這些值: 0: None 1: capturing phase 2: target phase 3: bubbling phase |
當 MouseEvent (滑鼠事件) 發生時,Event Object 有幾個常用的屬性:
屬性 | 說明 |
---|---|
which | 當按下滑鼠按鍵,取得是哪個按鍵,可能的值有: 0: 非按鍵 1: 左鍵 2: 中鍵或滾輪 3: 右鍵 |
relatedTarget | 指向參與事件的相關 DOM element。用在 mouseover 事件,表示剛離開的那個 DOM element;用在 mouseout 事件,表示剛進入的那個 DOM element |
pageX | 當按下滑鼠時 (或觸控螢幕時),取得距離頁面 (document) 最左上角的水平距離 (單位 pixel) |
pageY | 當按下滑鼠時 (或觸控螢幕時),取得距離頁面 (document) 最左上角的垂直距離 (單位 pixel) |
當 KeyboardEvent (鍵盤事件) 發生時,Event Object 有幾個常用的屬性:
屬性 | 說明 |
---|---|
keyCode | 當 keypress 事件時,返回 character code;當 keydown 或 keyup 事件時,返回 key code |
which | 值同 keyCode |
charCode | 當 keypress 事件時,返回 character code |
altKey | 布林值 (boolean),用來判斷使用者是否有按 alt 鍵 |
ctrlKey | 布林值 (boolean),用來判斷使用者是否有按 ctrl 鍵 |
metaKey | 布林值 (boolean),用來判斷使用者是否有按 meta 鍵 |
shiftKey | 布林值 (boolean),用來判斷使用者是否有按 shift 鍵 |
event.stopPropagation()
了解事件傳播的概念後,你可能會想到,那我怎麼不要讓事件傳播下去?答案就是使用 event object 的 stopPropagation 方法。
例子:
element.addEventListener('click', function(event) {
event.stopPropagation();
// ...
});
event.cancelBubble = true;
。event.preventDefault()
event object 有一個 preventDefault 方法用來取消瀏覽器預設的行為。
瀏覽器預設行為舉例來說像是:
- 點擊一個超連結後,會載入新的頁面
- 在表單輸入欄位中輸入 enter 會送出表單
例如:
element.addEventListener('click', function(event) {
event.preventDefault();
// ...
});
event.returnValue = false;
。常見的事件處理範例 (Examples)
onclick event
// 當使用者用滑鼠點擊螢幕 (或用手點擊觸控螢幕) 時
document.body.addEventListener('click', function(event) {
// 把字體改成黃色
event.target.style.color = 'yellow';
});
onkeydown event
// 當使用者在網頁中按下鍵盤按鍵時
document.onkeydown = function(event) {
if (event.keyCode === 89 && event.ctrlKey) {
alert('你同時按下 "control + y"');
} else if (event.which === 90 && event.ctrlKey ){
alert('你同時按下 "control + z"');
}
};
onbeforeunload event
// 當使用者要離開或關閉目前頁面時
window.onbeforeunload = function(event) {
// 返回要顯示給使用者看的提醒文字
return '你確定要離開本頁面嗎?';
};