事件簡介
事件是在您正在程式設計的系統中發生的事件,系統會將這些事件通知您,以便您的程式碼可以對它們做出反應。
例如,如果使用者點選網頁上的按鈕,您可能希望透過顯示資訊框來對該操作做出反應。在本文中,我們將討論圍繞事件的一些重要概念,並瞭解它們在瀏覽器中的工作原理。這不會是一項詳盡的研究;只是您在此階段需要了解的內容。
| 先決條件 | 對 HTML、CSS 和 JavaScript 初步知識 的基本瞭解。 |
|---|---|
| 目標 | 瞭解事件的基本理論、它們在瀏覽器中的工作原理以及事件在不同程式設計環境中的差異。 |
什麼是事件?
事件是在您正在程式設計的系統中發生的事件——當事件發生時,系統會發出某種訊號(或“觸發”),並提供一種機制,以便在事件發生時自動執行某個操作(即執行某些程式碼)。事件在瀏覽器視窗內觸發,並且往往與駐留在其中的特定專案相關聯。這可能是一個單獨的元素、一組元素、當前選項卡中載入的 HTML 文件或整個瀏覽器視窗。可以發生許多不同型別的事件。
例如
- 使用者選擇、點選或將游標懸停在某個元素上。
- 使用者選擇鍵盤上的某個鍵。
- 使用者調整瀏覽器視窗大小或關閉瀏覽器視窗。
- 網頁載入完成。
- 提交表單。
- 播放、暫停或結束影片。
- 發生錯誤。
您可以從這裡(以及瀏覽 MDN 的 事件參考)中瞭解到,可以觸發 **很多** 事件。
要對事件做出反應,您需要向其附加一個 **事件處理程式**。這是一個程式碼塊(通常是您作為程式設計師建立的 JavaScript 函式),當事件觸發時執行。當定義此類程式碼塊以響應事件執行時,我們說我們正在 **註冊事件處理程式**。注意:事件處理程式有時被稱為 **事件偵聽器**——就我們的目的而言,它們幾乎可以互換使用,儘管嚴格來說,它們是協同工作的。偵聽器偵聽事件的發生,而處理程式是在事件發生時執行的程式碼。
注意:Web 事件不是核心 JavaScript 語言的一部分——它們被定義為瀏覽器內建 API 的一部分。
一個示例:處理點選事件
在下面的示例中,我們在頁面中有一個單獨的 <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;
});
HTML <button> 元素在使用者點選按鈕時會觸發事件。因此它定義了一個 addEventListener() 函式,我們在這裡呼叫它。我們傳遞了兩個引數
- 字串
"click",表示我們想要監聽點選事件。按鈕可以觸發許多其他事件,例如"mouseover"(當用戶將滑鼠懸停在按鈕上時)或"keydown"(當用戶按下某個鍵並且按鈕處於焦點狀態時)。 - 當事件發生時要呼叫的函式。在我們的例子中,該函式生成一個隨機的 RGB 顏色,並將頁面的
background-color設定為該顏色<body>。
將處理程式函式作為單獨的命名函式是可以的,如下所示
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() 方法將其再次移除。例如,這將移除 changeBackground() 事件處理程式
btn.removeEventListener("click", changeBackground);
還可以透過將 AbortSignal 傳遞給 addEventListener(),然後稍後在擁有 AbortSignal 的控制器上呼叫 abort() 來移除事件處理程式。例如,要新增一個可以使用 AbortSignal 移除的事件處理程式
const controller = new AbortController();
btn.addEventListener("click",
() => {
const rndCol = `rgb(${random(255)} ${random(255)} ${random(255)})`;
document.body.style.backgroundColor = rndCol;
},
{ signal: controller.signal } // pass an AbortSignal to this handler
);
然後,上面程式碼建立的事件處理程式可以像這樣移除
controller.abort(); // removes any/all event handlers associated with this controller
對於簡單的小程式,清理舊的、未使用的事件處理程式不是必需的,但對於更大、更復雜程式,它可以提高效率。此外,移除事件處理程式的功能使您可以讓同一個按鈕在不同的情況下執行不同的操作:您只需新增或移除處理程式即可。
為單個事件新增多個偵聽器
透過多次呼叫 addEventListener() 並提供不同的處理程式,您可以為單個事件提供多個處理程式
myElement.addEventListener("click", functionA);
myElement.addEventListener("click", functionB);
現在,當元素被點選時,這兩個函式都會執行。
瞭解更多
addEventListener() 提供了其他強大的功能和選項。
這些超出了本文的範圍,但如果您想閱讀它們,請訪問 addEventListener() 和 removeEventListener() 參考頁面。
其他事件監聽器機制
我們建議您使用 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('click', handler),並在第二個引數中指定不同的函式
element.addEventListener("click", function1);
element.addEventListener("click", function2);
使用事件處理程式屬性這是不可能的,因為任何後續嘗試設定屬性都會覆蓋之前的屬性
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,它是一個專門的Event物件,具有一個key屬性,該屬性告訴您按下了哪個鍵。
<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>
<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事件(當表單提交時,會在表單上觸發提交事件)實現了一個非常簡單的檢查,該檢查測試文字欄位是否為空。如果為空,我們在事件物件上呼叫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()來註冊在執行一次後登出的事件偵聽器。HTTP connect 事件文件提供了一個很好的示例。
您還可以使用 JavaScript 構建跨瀏覽器外掛——瀏覽器功能增強——使用稱為WebExtensions的技術。事件模型類似於 Web 事件模型,但略有不同——事件偵聽器的屬性採用駝峰式大小寫(例如onMessage而不是onmessage),並且需要與addListener函式結合使用。請參閱runtime.onMessage頁面以獲取示例。
在這個學習階段,您無需瞭解其他此類環境的任何資訊;我們只是想清楚地表明,事件在不同的程式設計環境中可能有所不同。