事件介紹
事件是你在程式設計的系統中發生的事情,系統會告訴你這些事情,以便你的程式碼可以對它們做出反應。例如,如果使用者點選網頁上的按鈕,你可能希望透過顯示資訊框來對此操作做出反應。在本文中,我們將討論圍繞事件的一些重要概念,並介紹它們在瀏覽器中工作的基礎知識。
什麼是事件?
事件是你在程式設計的系統中發生的事情——當事件發生時,系統會產生(或“觸發”)某種訊號,並提供一種機制,透過該機制,當事件發生時可以自動採取行動(即執行一些程式碼)。事件在瀏覽器視窗內觸發,並且往往附加到其中存在的特定專案。這可能是一個元素、一組元素、當前標籤頁中載入的 HTML 文件,或者整個瀏覽器視窗。可以發生許多不同型別的事件。
例如
- 使用者選擇、點選或將游標懸停在某個元素上。
- 使用者按下鍵盤上的一個鍵。
- 使用者調整大小或關閉瀏覽器視窗。
- 網頁載入完成。
- 表單已提交。
- 影片正在播放、暫停或結束。
- 發生錯誤。
你可以從這裡(並從瀏覽事件索引)瞭解到,可以觸發的事件非常多。
要對事件做出反應,你需要為其附加一個事件監聽器。這是一個監聽事件觸發的程式碼功能。當事件觸發時,會呼叫一個事件處理程式函式(由事件監聽器引用或包含在事件監聽器中)來響應事件的觸發。當這樣一段程式碼被設定為響應事件而執行時,我們稱之為註冊事件處理程式。
一個例子:處理點選事件
在以下示例中,頁面上有一個<button>
<button>Change color</button>
然後我們有一些 JavaScript。我們將在下一節中更詳細地介紹它,但現在我們只需說:它為按鈕的"click"事件添加了一個事件監聽器,並且其中包含的處理程式函式透過將頁面背景設定為隨機顏色來響應事件
const btn = document.querySelector("button");
function random(number) {
return Math.floor(Math.random() * (number + 1));
}
btn.addEventListener("click", () => {
const rndCol = `rgb(${random(255)} ${random(255)} ${random(255)})`;
document.body.style.backgroundColor = rndCol;
});
示例輸出如下。嘗試點選按鈕
使用 addEventListener()
正如我們在上一個示例中看到的,可以觸發事件的物件具有 addEventListener() 方法,這是新增事件監聽器的推薦機制。
讓我們仔細看看上一個示例中的程式碼
const btn = document.querySelector("button");
function random(number) {
return Math.floor(Math.random() * (number + 1));
}
btn.addEventListener("click", () => {
const rndCol = `rgb(${random(255)} ${random(255)} ${random(255)})`;
document.body.style.backgroundColor = rndCol;
});
當用戶點選 <button> HTML 元素時,它將觸發一個 click 事件。我們呼叫其 addEventListener() 方法來新增事件監聽器;它接受兩個引數
- 字串
"click",表示我們要監聽click事件。按鈕可以觸發許多其他事件,例如當用戶將滑鼠移到按鈕上方時的"mouseover",或者當用戶按下鍵並且按鈕被聚焦時的"keydown"。 - 事件發生時呼叫的函式。在我們的示例中,定義的匿名函式生成一個隨機 RGB 顏色,並將頁面
<body>的background-color設定為該顏色。
你也可以建立一個單獨的命名函式,並在 addEventListener() 的第二個引數中引用它,如下所示
const btn = document.querySelector("button");
function random(number) {
return Math.floor(Math.random() * (number + 1));
}
function changeBackground() {
const rndCol = `rgb(${random(255)} ${random(255)} ${random(255)})`;
document.body.style.backgroundColor = rndCol;
}
btn.addEventListener("click", changeBackground);
監聽其他事件
按鈕元素可以觸發許多不同的事件。讓我們做個實驗。
首先,在本地複製一份 random-color-addeventlistener.html,並在瀏覽器中開啟它。這只是我們已經玩過的簡單隨機顏色示例的副本。現在嘗試依次將 click 更改為以下不同的值,並觀察示例中的結果
focus和blur— 按鈕獲得焦點和失去焦點時顏色會改變;嘗試按 Tab 鍵聚焦按鈕,然後再次按 Tab 鍵使焦點離開按鈕。這些通常用於在表單欄位獲得焦點時顯示有關填寫表單欄位的資訊,或者在表單欄位填寫了不正確的值時顯示錯誤訊息。dblclick— 只有雙擊按鈕時顏色才會改變。mouseover和mouseout— 滑鼠指標懸停在按鈕上方時顏色會改變,或者當指標移開按鈕時顏色會改變。
某些事件(例如 click)幾乎在任何元素上都可用。其他事件則更具特異性,僅在某些情況下有用:例如,play 事件僅適用於具有播放功能的元素,例如 <video>。
刪除監聽器
如果你使用 addEventListener() 添加了事件監聽器,如果需要,你可以再次將其刪除。最常見的方法是使用 removeEventListener() 方法。例如,以下行將刪除我們之前看到的 click 事件處理程式
btn.removeEventListener("click", changeBackground);
對於簡單的小程式,清理舊的、未使用的事件處理程式不是必需的,但對於更大、更復雜的程式,它可以提高效率。此外,刪除事件處理程式的能力允許你在不同情況下讓同一個按鈕執行不同的操作:你所要做的就是新增或刪除處理程式。
為單個事件新增多個監聽器
透過多次呼叫 addEventListener(),提供不同的處理程式,你可以讓多個處理程式函式響應單個事件執行
myElement.addEventListener("click", functionA);
myElement.addEventListener("click", functionB);
現在,當元素被點選時,這兩個函式都會執行。
其他事件監聽機制
我們建議你使用 addEventListener() 註冊事件處理程式。它是最強大的方法,並且最能適應更復雜的程式。但是,你可能會看到另外兩種註冊事件處理程式的方法:事件處理程式屬性和內聯事件處理程式。
事件處理程式屬性
可以觸發事件的物件(例如按鈕)通常也具有屬性,其名稱是 on 後跟事件名稱。例如,元素具有 onclick 屬性。這稱為事件處理程式屬性。要監聽事件,你可以將處理程式函式分配給該屬性。
例如,我們可以這樣重寫隨機顏色示例
const btn = document.querySelector("button");
function random(number) {
return Math.floor(Math.random() * (number + 1));
}
btn.onclick = () => {
const rndCol = `rgb(${random(255)} ${random(255)} ${random(255)})`;
document.body.style.backgroundColor = rndCol;
};
你也可以將處理程式屬性設定為命名函式
const btn = document.querySelector("button");
function random(number) {
return Math.floor(Math.random() * (number + 1));
}
function bgChange() {
const rndCol = `rgb(${random(255)} ${random(255)} ${random(255)})`;
document.body.style.backgroundColor = rndCol;
}
btn.onclick = bgChange;
與 addEventListener() 相比,事件處理程式屬性具有缺點。其中最重要的是你不能為單個事件新增多個監聽器。以下模式不起作用,因為任何後續設定屬性值的嘗試都會覆蓋前面的值
element.onclick = function1;
element.onclick = function2;
內聯事件處理程式——不要使用這些
你可能還會在程式碼中看到這樣的模式
<button onclick="bgChange()">Press me</button>
function bgChange() {
const rndCol = `rgb(${random(255)} ${random(255)} ${random(255)})`;
document.body.style.backgroundColor = rndCol;
}
Web 上發現的最早的註冊事件處理程式的方法涉及事件處理程式 HTML 屬性(或內聯事件處理程式),如上面所示——屬性值包含你希望事件發生時執行的 JavaScript 程式碼。上面的示例呼叫了在同一頁面上的 <script> 元素中定義的函式,但你也可以將 JavaScript 直接插入到屬性中,例如
<button onclick="alert('Hello, this is my old-fashioned event handler!');">
Press me
</button>
你可以找到許多事件處理程式屬性的 HTML 屬性等價物;但是,你不應該使用這些——它們被認為是糟糕的做法。如果你正在做一些非常快速的事情,使用事件處理程式屬性可能看起來很容易,但它們很快就會變得難以管理和低效。
首先,將 HTML 和 JavaScript 混在一起不是一個好主意,因為它會變得難以閱讀。將 JavaScript 分開是一個好習慣,如果它在一個單獨的檔案中,你可以將其應用於多個 HTML 文件。
即使在單個檔案中,內聯事件處理程式也不是一個好主意。一個按鈕還可以,但是如果你有 100 個按鈕怎麼辦?你必須向檔案中新增 100 個屬性;它會很快變成一場維護噩夢。使用 JavaScript,你可以輕鬆地將事件處理程式函式新增到頁面上的所有按鈕,無論有多少個,使用如下程式碼
const buttons = document.querySelectorAll("button");
for (const button of buttons) {
button.addEventListener("click", bgChange);
}
最後,許多常見的伺服器配置會作為安全措施禁止內聯 JavaScript。
你絕不應該使用 HTML 事件處理程式屬性——它們已過時,並且使用它們是糟糕的做法。
事件物件
有時,在事件處理程式函式中,你會看到一個引數被指定為 event、evt 或 e 等名稱。這被稱為事件物件,它會自動傳遞給事件處理程式,以提供額外的功能和資訊。例如,讓我們重寫我們的隨機顏色示例以包含一個事件物件
const btn = document.querySelector("button");
function random(number) {
return Math.floor(Math.random() * (number + 1));
}
function bgChange(e) {
const rndCol = `rgb(${random(255)} ${random(255)} ${random(255)})`;
e.target.style.backgroundColor = rndCol;
console.log(e);
}
btn.addEventListener("click", bgChange);
在這裡你可以看到我們在函式中包含了一個事件物件 e,並在函式中為 e.target 設定了背景顏色樣式——也就是按鈕本身。事件物件的 target 屬性總是對事件發生元素的引用。所以,在這個例子中,我們為按鈕設定了一個隨機背景顏色,而不是頁面。
注意:你可以為事件物件使用任何你喜歡的名稱——你只需要選擇一個可以在事件處理程式函式中引用的名稱。e、evt 和 event 是開發人員常用的名稱,因為它們簡短易記。保持一致性總是好的——與你自己,如果可能的話,也與其他人保持一致。
事件物件的額外屬性
大多數事件物件都有一組標準的屬性和方法可用;請參閱 Event 物件參考以獲取完整列表。
某些事件物件會新增與該特定事件型別相關的額外屬性。例如,當用戶按下鍵時會觸發 keydown 事件。它的事件物件是 KeyboardEvent,它是一個帶有 key 屬性的專用 Event 物件,該屬性會告訴你按下了哪個鍵
<input id="textBox" type="text" />
<div id="output"></div>
const textBox = document.querySelector("#textBox");
const output = document.querySelector("#output");
textBox.addEventListener("keydown", (event) => {
output.textContent = `You pressed "${event.key}".`;
});
嘗試在文字框中輸入並檢視輸出
阻止預設行為
有時,你會遇到這樣一種情況:你希望阻止事件執行其預設操作。最常見的例子是 Web 表單,例如自定義登錄檔單。當你填寫詳細資訊並點選提交按鈕時,自然行為是將資料提交到伺服器上指定的頁面進行處理,並且瀏覽器會重定向到某種“成功訊息”頁面(如果未指定其他頁面,則為同一頁面)。
當用戶未正確提交資料時,就會出現問題——作為開發人員,你希望阻止提交到伺服器並給出錯誤訊息,說明問題所在以及需要做什麼才能解決問題。一些瀏覽器支援自動錶單資料驗證功能,但由於許多瀏覽器不支援,建議不要依賴這些功能並實現自己的驗證檢查。讓我們看一個例子。
首先,一個簡單的 HTML 表單,要求你輸入名字和姓氏
<form action="#">
<div>
<label for="fname">First name: </label>
<input id="fname" type="text" />
</div>
<div>
<label for="lname">Last name: </label>
<input id="lname" type="text" />
</div>
<div>
<input id="submit" type="submit" />
</div>
</form>
<p></p>
現在是 JavaScript——在這裡,我們在 submit 事件(表單提交時觸發 submit 事件)的處理程式中實現了一個基本檢查,它測試文字欄位是否為空。如果為空,我們會在事件物件上呼叫 preventDefault() 函式——這會阻止表單提交——然後在表單下方的段落中顯示一條錯誤訊息,告訴使用者哪裡出錯了
const form = document.querySelector("form");
const fname = document.getElementById("fname");
const lname = document.getElementById("lname");
const para = document.querySelector("p");
form.addEventListener("submit", (e) => {
if (fname.value === "" || lname.value === "") {
e.preventDefault();
para.textContent = "You need to fill in both names!";
}
});
顯然,這種表單驗證相當薄弱——例如,它不會阻止使用者在欄位中輸入空格或數字來驗證表單——但它適用於示例目的。
你可以在即時執行中檢視完整示例——在那裡試用一下。有關完整原始碼,請參閱preventdefault-validation.html。
不僅僅是網頁
事件並非 JavaScript 獨有——大多數程式語言都有某種事件模型,並且該模型的工作方式通常與 JavaScript 的方式不同。事實上,JavaScript 在網頁中的事件模型與 JavaScript 在其他環境中的事件模型有所不同。
例如,Node.js 是一個非常流行的 JavaScript 執行時,它使開發人員能夠使用 JavaScript 構建網路和伺服器端應用程式。Node.js 事件模型依賴於監聽器監聽事件和發射器定期發射事件——聽起來沒什麼不同,但程式碼卻大相徑庭,它使用了 on() 等函式來註冊事件監聽器,以及 once() 來註冊一個在執行一次後就登出的事件監聽器。Node.js HTTP connect 事件文件提供了一個很好的例子。
你還可以使用 JavaScript 構建跨瀏覽器附加元件——瀏覽器功能增強——使用一種名為 WebExtensions 的技術。事件模型類似於 Web 事件模型,但略有不同——事件監聽器屬性以駝峰式命名法編寫(例如 onMessage 而不是 onmessage),並且需要與 addListener 函式結合使用。請參閱 runtime.onMessage 頁面以獲取示例。
在學習的這個階段,你不需要了解其他此類環境的任何資訊;我們只是想明確一點,事件在不同的程式設計環境中可能有所不同。
總結
在本章中,我們學習了什麼是事件,如何監聽事件以及如何響應事件。
你已經看到,網頁中的元素可以巢狀在其他元素中。例如,在阻止預設行為示例中,我們有一些文字框,它們放置在 <div> 元素中,而這些元素又放置在 <form> 元素中。當一個點選事件監聽器附加到 <form> 元素上,並且使用者點選其中一個文字框內部時會發生什麼?相關的事件處理程式函式仍然透過一個稱為事件冒泡的過程觸發,這將在下一課中介紹。