網頁應用程式本身就是事件驅動,透過使用者的操作或系統的事件,在適當的時候作些事情。在事件標準化之前,就存在於各瀏覽器的一個事件模型,稱為基本事件模型(Basic Event Model),雖然沒有標準化,但因相對(於 DOM Level 2 事件與 Internet Explorer 事件模型)來說,有比較跨瀏覽器的一致性,因此是大多數人最熟悉且最常用的事件模型。
在基本事件模型中,要在某個事件發生時,呼叫指定的函式,是將函式指定給某個特性。例如,要在網頁文件準備好,所有資源都載入後作些事情,可以註冊 window
的 load
事件,方式就是將函式指定給 window.onload
特性,這在之前的範例中看過:
window.onload = function() {
// onload 事件發生時要作的事...
};
例如,要在按鈕的 click
事件發生時作些事,可以指定函式給按鈕元素的 onclick
特性:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
<button id="btn1">按鈕一</button><br>
<button id="btn2">按鈕二</button><br>
<div id="console"></div>
<script type="text/javascript">
function handler() {
document.getElementById('console').innerHTML
= `Who's clicked: ${this.id}`;
}
document.getElementById('btn1').onclick = handler;
document.getElementById('btn2').onclick = handler;
</script>
</body>
</html>
在上例中,用 handler
函式註冊了兩個按鈕的 click
事件,事件在元素上觸發而呼叫函式時,this
就會設定為當時觸發事件的元素。
在這個例子中,當你按下按鈕時而呼叫函式時,this
就參考至當時按下的按鈕。像以上的作法,稱之為傳統模型(Traditional model)或傳統註冊模型(Traditional registration model)。
儘管現在已不建議接下來的作法,但在過去開發的網頁程式中經常看到的作法就是,在標籤的屬性上撰寫 JavaScript 作為觸發事件時要執行的程式,這樣的作法稱之為行內模型(Inline model)或行內註冊模型(Inline registration model)。例如:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script type="text/javascript">
function handle(elem) {
document.getElementById('console').innerHTML
= `Who's clicked: ${elem.id}`;
}
</script>
</head>
<body>
<button id="btn1" onclick="handle(this);">按鈕一</button><br>
<button id="btn2" onclick="handle(this);">按鈕二</button><br>
<div id="console"></div>
</body>
</html>
在標籤屬性上撰寫程式碼,儘管只有一小段,但仍算是在畫面中侵入程式碼,現在已不鼓勵這樣的作法。在上例中,並非直接指定 click
事件的處理器函式,事實上,在標籤上指定程式碼的作法,會自動建立匿名函式,也就是說,上例相當於:
document.getElementById('btn1').onclick = function() {
handle(this);
};
你指定的程式碼,會成為匿名函式的本體內容,這也說明了上例中,this
其實就是指觸發事件時的元素。
有些事件觸發時會有預設的動作,例如表單的 submit
事件預設會發送表單,超鏈結的 click
事件預設會連結至指定網頁。在基本事件模型中,若要取消預設事件動作,可以讓事件處理器傳回 false
。例如:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
<form name="form1" action="fake.do">
輸入資料:<input name="data"><br>
<button type="submit">送出</button>
</form>
<script type="text/javascript">
document.form1.onsubmit = function() {
if(this.data.value.length === 0) {
return false;
}
};
</script>
</body>
</html>
在上例中,按下按鈕會觸發表單的 submit
事件,你檢查欄位是否有填值,沒有的話就在事件處理器中傳回 false
,這會取消表單的發送,而這就是表單驗證的基本作法。若是使用行內模型。就會看到這種作法:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script type="text/javascript">
function validate(form) {
if(form.data.value.length === 0) {
return false;
}
return true;
}
</script>
</head>
<body>
<form name="form1" action="fake.do" onsubmit="return validate(this);">
輸入資料:<input name="data"><br>
<button type="submit">送出</button>
</form>
</body>
</html>
要在使用者離開頁面時,做一個簡單的確認,若使用行內模型,可以如下:
<a href="http://caterpillar.onlyfun.net"
onclick="return confirm('要離開了嗎?');">首頁</a>
事件不一定要由使用者的操作觸發,也可以直接呼叫方法來觸發事件。例如可以呼叫表單元素的 submit
方法,如果在載入頁面時,想要讓第一個輸入欄位取得焦點,可以呼叫輸入欄位元素的 focus
方法等。
例如,如果某個輸入欄位 id
為 user
,則可以如下在頁面資源全部載入後,讓該欄位取得焦點:
window.onload = function() {
document.getElementById('test').focus();
};
如果你使用 ES6 的箭號函式來做為事件處理器,必須特別注意的是,箭號函式的 this
是根據語彙環境,而不是像 function
根據呼叫者是誰來決定。