事件是表示某事已發生的訊號。所有 DOM 節點都會產生此類訊號(但事件不限於 DOM)。
以下列出最實用的 DOM 事件清單,供您參考
滑鼠事件
click
– 當滑鼠按一下元素時(觸控螢幕裝置會在輕觸時產生此事件)。contextmenu
– 當滑鼠在元素上按右鍵時。mouseover
/mouseout
– 當滑鼠游標移到 / 離開元素時。mousedown
/mouseup
– 當滑鼠按鈕在元素上按下 / 釋放時。mousemove
– 當滑鼠移動時。
鍵盤事件
keydown
和keyup
– 當鍵盤按鍵按下和釋放時。
表單元素事件
submit
– 當訪客提交<form>
時。focus
– 當訪客聚焦在元素上,例如<input>
。
文件事件
DOMContentLoaded
– 當 HTML 已載入和處理完畢,DOM 已完全建立時。
CSS 事件
transitionend
– 當 CSS 動畫完成時。
還有許多其他事件。我們將在後面的章節中深入探討特定事件。
事件處理常式
為了對事件做出反應,我們可以指定一個處理常式 – 一個在事件發生時執行的函式。
處理常式是一種在使用者操作時執行 JavaScript 程式碼的方法。
有幾種指定處理常式的方法。讓我們從最簡單的方法開始。
HTML 屬性
可以在 HTML 中使用名為 on<event>
的屬性設定處理常式。
例如,要為 input
指定 click
處理常式,我們可以使用 onclick
,如下所示
<input value="Click me" onclick="alert('Click!')" type="button">
滑鼠按一下時,onclick
內的程式碼就會執行。
請注意,在 onclick
內我們使用單引號,因為屬性本身是用雙引號。如果我們忘記程式碼在屬性內,並在其中使用雙引號,如下所示:onclick="alert("Click!")"
,則它將無法正常運作。
HTML 屬性不是撰寫大量程式碼的便利位置,因此我們最好建立一個 JavaScript 函式並在那裡呼叫它。
這裡的按一下會執行函式 countRabbits()
<script>
function countRabbits() {
for(let i=1; i<=3; i++) {
alert("Rabbit number " + i);
}
}
</script>
<input type="button" onclick="countRabbits()" value="Count rabbits!">
如我們所知,HTML 屬性名稱不區分大小寫,因此 ONCLICK
與 onClick
和 onCLICK
… 一樣有效,但通常屬性會使用小寫:onclick
。
DOM 屬性
我們可以使用 DOM 屬性 on<event>
指定處理常式。
例如,elem.onclick
<input id="elem" type="button" value="Click me">
<script>
elem.onclick = function() {
alert('Thank you');
};
</script>
如果處理常式是使用 HTML 屬性指定的,則瀏覽器會讀取它,從屬性內容建立一個新函式,並將它寫入 DOM 屬性。
所以這種方式實際上與前一種方式相同。
這兩個程式碼片段運作方式相同
-
只有 HTML
<input type="button" onclick="alert('Click!')" value="Button">
-
HTML + JS
<input type="button" id="button" value="Button"> <script> button.onclick = function() { alert('Click!'); }; </script>
在第一個範例中,HTML 屬性用於初始化 button.onclick
,而在第二個範例中,則是用腳本,這就是全部的差別。
由於只有一個 onclick
屬性,因此我們無法指定多個事件處理常式。
在以下範例中,使用 JavaScript 新增處理常式會覆寫現有的處理常式
<input type="button" id="elem" onclick="alert('Before')" value="Click me">
<script>
elem.onclick = function() { // overwrites the existing handler
alert('After'); // only this will be shown
};
</script>
若要移除處理常式,請指定 elem.onclick = null
。
存取元素:this
處理常式內 this
的值是元素。就是具有處理常式的元素。
在以下程式碼中,button
使用 this.innerHTML
顯示其內容
<button onclick="alert(this.innerHTML)">Click me</button>
可能的錯誤
如果您開始使用事件,請注意一些細微差別。
我們可以將現有函式設定為處理常式
function sayThanks() {
alert('Thanks!');
}
elem.onclick = sayThanks;
但請小心:函式應指定為 sayThanks
,而不是 sayThanks()
。
// right
button.onclick = sayThanks;
// wrong
button.onclick = sayThanks();
如果我們加上括號,則 sayThanks()
會變成函式呼叫。因此,最後一行實際上會取得函式執行結果,也就是 undefined
(因為函式沒有傳回任何內容),並將其指定給 onclick
。這無法運作。
…另一方面,在標記中,我們確實需要括號
<input type="button" id="button" onclick="sayThanks()">
這個差別很容易解釋。當瀏覽器讀取屬性時,它會從屬性內容建立一個主體具有處理常式函式的函式。
因此,標記會產生這個屬性
button.onclick = function() {
sayThanks(); // <-- the attribute content goes here
};
請勿對處理常式使用 setAttribute
。
這樣的呼叫無法運作
// a click on <body> will generate errors,
// because attributes are always strings, function becomes a string
document.body.setAttribute('onclick', function() { alert(1) });
DOM 屬性會區分大小寫。
將處理常式指定給 elem.onclick
,而不是 elem.ONCLICK
,因為 DOM 屬性會區分大小寫。
addEventListener
上述指定處理常式方式的基本問題是,我們無法將多個處理常式指定給一個事件。
假設我們的程式碼一部分想要在按一下時突顯按鈕,而另一部分想要在按一下時顯示訊息。
我們想要為此指定兩個事件處理常式。但新的 DOM 屬性會覆寫現有的屬性
input.onclick = function() { alert(1); }
// ...
input.onclick = function() { alert(2); } // replaces the previous handler
網頁標準開發人員很早以前就了解到這一點,並建議使用特殊方法 addEventListener
和 removeEventListener
來管理處理常式,這些方法不受此限制。
新增處理常式的語法
element.addEventListener(event, handler, [options]);
事件
- 事件名稱,例如
"click"
。 處理常式
- 處理常式函式。
選項
- 具有下列屬性的附加選用物件
若要移除處理常式,請使用 removeEventListener
element.removeEventListener(event, handler, [options]);
若要移除處理常式,我們應傳遞與指定函式完全相同的函式。
這無法運作
elem.addEventListener( "click" , () => alert('Thanks!'));
// ....
elem.removeEventListener( "click", () => alert('Thanks!'));
處理常式不會被移除,因為 removeEventListener
會取得另一個函式,其具有相同的程式碼,但這並不重要,因為它是不同的函式物件。
以下是正確的方式
function handler() {
alert( 'Thanks!' );
}
input.addEventListener("click", handler);
// ....
input.removeEventListener("click", handler);
請注意,如果我們未將函式儲存在變數中,則無法移除它。沒有辦法「讀回」由 addEventListener
指定的處理常式。
多次呼叫 addEventListener
可讓它新增多個處理常式,如下所示
<input id="elem" type="button" value="Click me"/>
<script>
function handler1() {
alert('Thanks!');
};
function handler2() {
alert('Thanks again!');
}
elem.onclick = () => alert("Hello");
elem.addEventListener("click", handler1); // Thanks!
elem.addEventListener("click", handler2); // Thanks again!
</script>
如上例所示,我們可以使用 DOM 屬性和 addEventListener
同時 設定處理常式。但通常我們只會使用其中一種方式。
addEventListener
有些事件無法透過 DOM 屬性指定。只能使用 addEventListener
。
例如,DOMContentLoaded
事件,會在載入文件並建立 DOM 時觸發。
// will never run
document.onDOMContentLoaded = function() {
alert("DOM built");
};
// this way it works
document.addEventListener("DOMContentLoaded", function() {
alert("DOM built");
});
因此,addEventListener
較為通用。儘管如此,此類事件是例外,而非常態。
事件物件
若要妥善處理事件,我們會希望進一步瞭解發生了什麼事。不只是「按一下」或「按下鍵」,而是指標座標為何?按下了哪個鍵?等等。
當事件發生時,瀏覽器會建立一個事件物件,將詳細資訊放入其中,並將其作為引數傳遞給處理常式。
以下是如何從事件物件取得指標座標的範例
<input type="button" value="Click me" id="elem">
<script>
elem.onclick = function(event) {
// show event type, element and coordinates of the click
alert(event.type + " at " + event.currentTarget);
alert("Coordinates: " + event.clientX + ":" + event.clientY);
};
</script>
event
物件的一些屬性
event.type
- 事件類型,這裡是
"click"
。 event.currentTarget
- 處理事件的元素。這與
this
完全相同,除非處理常式是箭頭函式,或其this
繫結到其他項目,否則我們可以從event.currentTarget
取得元素。 event.clientX
/event.clientY
- 指標事件的游標相對於視窗的座標。
還有更多屬性。其中許多屬性取決於事件類型:鍵盤事件有一組屬性,指標事件有另一組屬性,我們會在探討不同事件的詳細資訊時再研究它們。
如果我們在 HTML 中指定處理常式,我們也可以使用 event
物件,如下所示
<input type="button" onclick="alert(event.type)" value="Event type">
這是可能的,因為當瀏覽器讀取屬性時,它會建立一個類似這樣的處理常式:function(event) { alert(event.type) }
。也就是說:它的第一個引數稱為 "event"
,而主體則取自屬性。
物件處理常式:handleEvent
我們可以使用 addEventListener
不僅指定函式,還可以指定物件作為事件處理常式。當事件發生時,會呼叫其 handleEvent
方法。
例如
<button id="elem">Click me</button>
<script>
let obj = {
handleEvent(event) {
alert(event.type + " at " + event.currentTarget);
}
};
elem.addEventListener('click', obj);
</script>
正如我們所見,當 addEventListener
接收到物件作為處理常式時,它會在事件發生時呼叫 obj.handleEvent(event)
。
我們也可以使用自訂類別的物件,如下所示
<button id="elem">Click me</button>
<script>
class Menu {
handleEvent(event) {
switch(event.type) {
case 'mousedown':
elem.innerHTML = "Mouse button pressed";
break;
case 'mouseup':
elem.innerHTML += "...and released.";
break;
}
}
}
let menu = new Menu();
elem.addEventListener('mousedown', menu);
elem.addEventListener('mouseup', menu);
</script>
這裡同一個物件處理這兩個事件。請注意,我們需要使用 addEventListener
明確設定要監聽的事件。menu
物件在此處僅取得 mousedown
和 mouseup
,而不是任何其他類型的事件。
handleEvent
方法不必自己執行所有工作。它可以呼叫其他特定於事件的方法,如下所示
<button id="elem">Click me</button>
<script>
class Menu {
handleEvent(event) {
// mousedown -> onMousedown
let method = 'on' + event.type[0].toUpperCase() + event.type.slice(1);
this[method](event);
}
onMousedown() {
elem.innerHTML = "Mouse button pressed";
}
onMouseup() {
elem.innerHTML += "...and released.";
}
}
let menu = new Menu();
elem.addEventListener('mousedown', menu);
elem.addEventListener('mouseup', menu);
</script>
現在事件處理常式已清楚區分,這可能更容易支援。
摘要
有 3 種指定事件處理常式的方法
- HTML 屬性:
onclick="..."
。 - DOM 屬性:
elem.onclick = function
。 - 方法:
elem.addEventListener(event, handler[, phase])
用於新增,removeEventListener
用於移除。
HTML 屬性使用較少,因為 HTML 標籤中間的 JavaScript 看起來有點奇怪和陌生。而且也無法在其中撰寫大量程式碼。
DOM 屬性可以使用,但我們無法為特定事件指定多個處理常式。在許多情況下,此限制並非迫切。
最後一種方法最靈活,但也是撰寫時間最長的方法。只有少數事件只能使用它,例如 transitionend
和 DOMContentLoaded
(將會介紹)。此外,addEventListener
支援物件作為事件處理常式。在這種情況下,在事件發生時會呼叫方法 handleEvent
。
不論您如何指定處理常式,它都會將事件物件作為第一個引數取得。該物件包含有關發生事件的詳細資料。
我們將在後續章節中進一步了解事件的一般資訊和不同類型的事件。
留言
<code>
標籤,若要插入多行,請將它們包覆在<pre>
標籤中,若要插入超過 10 行,請使用沙盒 (plnkr、jsbin、codepen…)