事件處理(Event Handling)#
事件(Event)是使用者或瀏覽器觸發的動作,例如點擊按鈕、輸入文字、頁面載入完成等。
JavaScript 透過 addEventListener 監聽這些事件,讓網頁能夠即時回應互動行為。
事件監聽#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| const btn = document.querySelector("#btn");
// addEventListener(推薦)
btn.addEventListener("click", function(event) {
console.log("按鈕被點擊了!");
console.log(event); // Event 物件
});
// 箭頭函式版本
btn.addEventListener("click", (e) => {
console.log("點擊座標:", e.clientX, e.clientY);
});
// 移除事件監聽(需傳入同一個函式參考)
function handleClick() {
console.log("點擊");
}
btn.addEventListener("click", handleClick);
btn.removeEventListener("click", handleClick);
|
常用事件類型#
滑鼠事件#
1
2
3
4
5
6
| el.addEventListener("click", e => {}); // 點擊
el.addEventListener("dblclick", e => {}); // 雙擊
el.addEventListener("mouseenter", e => {}); // 滑鼠進入
el.addEventListener("mouseleave", e => {}); // 滑鼠離開
el.addEventListener("mousemove", e => {}); // 滑鼠移動
el.addEventListener("contextmenu", e => {}); // 右鍵選單
|
鍵盤事件#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| document.addEventListener("keydown", e => {
console.log(e.key); // 按鍵名稱,如 "Enter"、"a"
console.log(e.code); // 實體按鍵,如 "KeyA"、"Enter"
console.log(e.ctrlKey); // 是否按住 Ctrl
if (e.key === "Enter") {
console.log("按下 Enter");
}
if (e.ctrlKey && e.key === "s") {
e.preventDefault(); // 阻止預設行為
console.log("儲存");
}
});
document.addEventListener("keyup", e => {});
|
表單事件#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| const form = document.querySelector("form");
const input = document.querySelector("input");
// 表單提交
form.addEventListener("submit", e => {
e.preventDefault(); // 阻止頁面重整
console.log("表單提交:", input.value);
});
// 輸入框變化
input.addEventListener("input", e => {
console.log("即時輸入:", e.target.value);
});
input.addEventListener("change", e => {
console.log("值改變(失焦後):", e.target.value);
});
input.addEventListener("focus", e => {
console.log("獲得焦點");
});
input.addEventListener("blur", e => {
console.log("失去焦點");
});
|
視窗事件#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| window.addEventListener("load", () => {
console.log("所有資源載入完成");
});
document.addEventListener("DOMContentLoaded", () => {
console.log("DOM 解析完成(不等圖片)");
});
window.addEventListener("resize", () => {
console.log("視窗大小:", window.innerWidth, window.innerHeight);
});
window.addEventListener("scroll", () => {
console.log("捲動位置:", window.scrollY);
});
|
Event 物件#
1
2
3
4
5
6
7
8
9
10
11
| document.addEventListener("click", e => {
e.target; // 觸發事件的元素
e.currentTarget; // 綁定事件的元素(可能不同)
e.type; // 事件類型,如 "click"
e.clientX; // 相對視窗的 X 座標
e.clientY; // 相對視窗的 Y 座標
e.pageX; // 相對整個頁面的 X 座標
e.preventDefault(); // 阻止預設行為(如連結跳轉)
e.stopPropagation(); // 阻止事件冒泡
});
|
事件冒泡(Event Bubbling)#
事件從觸發元素向上傳播到父元素:
1
2
3
4
5
| <div id="outer">
<div id="inner">
<button id="btn">點擊</button>
</div>
</div>
|
1
2
3
4
5
6
7
8
9
10
11
| document.querySelector("#btn").addEventListener("click", e => {
console.log("button"); // 1. 先觸發
});
document.querySelector("#inner").addEventListener("click", e => {
console.log("inner"); // 2. 再觸發
});
document.querySelector("#outer").addEventListener("click", e => {
console.log("outer"); // 3. 最後觸發
});
// 點擊按鈕輸出:button → inner → outer
|
事件委派(Event Delegation)#
利用冒泡機制,在父元素統一監聽子元素事件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // 不好的做法:對每個 li 個別綁定
document.querySelectorAll("li").forEach(li => {
li.addEventListener("click", e => {
li.classList.toggle("selected");
});
});
// 好的做法:事件委派到 ul(動態新增的 li 也會生效)
const list = document.querySelector("ul");
list.addEventListener("click", e => {
if (e.target.tagName === "LI") {
e.target.classList.toggle("selected");
}
});
|
只觸發一次#
1
2
3
| btn.addEventListener("click", () => {
console.log("只觸發一次");
}, { once: true });
|
實際範例:標籤頁切換#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| <div class="tabs">
<button class="tab-btn active" data-tab="tab1">標籤一</button>
<button class="tab-btn" data-tab="tab2">標籤二</button>
<button class="tab-btn" data-tab="tab3">標籤三</button>
</div>
<div id="tab1" class="tab-content">內容一</div>
<div id="tab2" class="tab-content" hidden>內容二</div>
<div id="tab3" class="tab-content" hidden>內容三</div>
<script>
const tabBtns = document.querySelectorAll(".tab-btn");
const tabContents = document.querySelectorAll(".tab-content");
tabBtns.forEach(btn => {
btn.addEventListener("click", () => {
// 移除所有 active
tabBtns.forEach(b => b.classList.remove("active"));
tabContents.forEach(c => c.hidden = true);
// 啟用目前的
btn.classList.add("active");
document.querySelector(`#${btn.dataset.tab}`).hidden = false;
});
});
</script>
|
Reference#