React 事件處理與事件傳遞:一篇搞懂冒泡、捕獲與阻止事件!

React 事件處理與事件傳遞:一篇搞懂冒泡、捕獲與阻止事件!

React 事件處理與事件傳遞:一篇搞懂冒泡、捕獲與阻止事件!

前言:

在開發React 時,「事件」是我們最常打交道的部分之一。無論是點擊按鈕、提交表單,還是操作其他互動元素,都需要處理事件。如果你剛接觸 React,可能會有以下疑問:

為什麼點擊按鈕時會觸發父層的事件?

什麼是事件的「冒泡」和「捕獲」?

如何攔截事件傳播或避免預設行為?

這篇文章將帶你從零開始理解 React 的事件處理,並解釋事件傳遞的原理及其在開發中的實際應用場景。

React 的事件處理基礎​

什麼是事件?​

根據 @MDN Web Docs - Introduction to events 對於事件的定義,事件是瀏覽器用來回應用戶操作的一種訊號。例如當你點擊按鈕、鍵入文字、或滑動滑鼠時,瀏覽器都會生成一個事件並傳遞給應用程式進行處理。

常見的事件類型包括:

滑鼠事件(Mouse Events):click, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout

鍵盤事件(Keyboard Events):keydown, keyup, keypress

表單事件(Form Events):submit, change, input, focus, blur

在 JavaScript 中,事件本身是一個由瀏覽器產生的事件物件(Event Object),它記錄了與互動相關的所有資訊,例如觸發的 HTML 元素、滑鼠座標、鍵盤按鍵等。

讓我們看看事件物件的基本樣貌和幾個常用屬性:

document.querySelector('button').addEventListener('click', (event) => { console.log(event.type); // 事件類型,例如 "click" console.log(event.target); // 觸發事件的 DOM 元素 console.log(event.currentTarget); // 綁定事件的 DOM 元素 console.log(event.clientX, event.clientY); // 滑鼠的 X、Y 座標});

HTML/JS 的事件處理方式​

在早期的網頁開發中,事件處理主要有以下幾種方式:

內聯屬性(Inline Attributes)

直接在 HTML 元素上綁定事件處理邏輯,這種方式直觀但不推薦,因為不易維護。

使用 DOM 的 addEventListener

更現代且推薦的方式是使用 JavaScript 的 addEventListener,可以將事件邏輯與 HTML 結構分離,提升可讀性和重用性。

const button = document.querySelector('button');button.addEventListener('click', () => { alert('Hello!');});

React 的事件處理方式​

React 的事件處理與原生 HTML 有顯著的不同,主要表現在以下幾點:

使用合成事件(Synthetic Event)

在 React 中,事件處理是基於一種叫做 合成事件(Synthetic Event) 的機制。合成事件是一層跨瀏覽器的事件封裝,提供了一個一致的介面。React 不會直接將事件綁定到每個 DOM 元素,而是採用事件委派的方式,將所有事件統一綁定到根節點(例如 document 或 root 元素)。

這樣做的好處是:

跨瀏覽器兼容性:自動處理不同瀏覽器之間的事件差異。

效能提升:React 使用事件委派機制,將所有事件綁定在根元素上,減少 DOM 操作。

function handleClick(event) { console.log(event.type); // "click"}

駝峰式命名

在 React 中,事件屬性使用駝峰式命名(Camel Case),例如 onClick、onMouseOver。

事件處理程式是函式引用

React 要求事件處理程式是函式的引用,而不是直接執行結果。這樣可以避免函式在渲染時被過早執行。

錯誤示範:

function handleClick() { alert('點擊了');} // handleClick 立即執行,錯誤!

正確寫法:

什麼是事件傳遞?捕獲與冒泡解讀​

在 JavaScript 事件模型中,當事件觸發時,並不僅僅作用於目標元素本身,還會按照一個固定的順序傳遞。這種事件傳遞機制分為兩個主要階段:捕獲階段 和 冒泡階段。

事件傳遞的階段​

事件在傳遞過程中會經歷以下三個階段:

捕獲階段(Capture Phase)

從 window 開始,沿著 DOM 樹向下傳遞到事件的目標元素。

目標階段(Target Phase)

事件到達目標元素,並執行與該元素相關的事件處理程式。

冒泡階段(Bubble Phase)

從目標元素開始,沿著 DOM 樹向上傳遞到 window。

這樣的流程讓開發者可以在事件傳遞的不同階段執行邏輯。

冒泡階段(Bubble Phase)​

冒泡指的是事件從目標元素向其祖先元素傳遞,直至 window 為止。當需要在父層統一處理子層的邏輯時,冒泡階段非常有用。

警告React 默認只處理冒泡階段的事件,

範例:父元素捕獲子元素的點擊事件

function handleParentClick(event) { console.log('父層事件觸發'); console.log('點擊的子元素是:', event.target);}

輸出結果:

當點擊「按鈕 1」時,event.target 是

執行順序:

handleCapture 執行(捕獲階段)。

handleBubble 執行(冒泡階段)。

捕獲與冒泡的實際應用​

看到這邊你可能會想:「我在日常開發中真的需要關心捕獲和冒泡嗎?不能一招 onClick 打天下嗎」答案是 視情況而定。以下我來舉一些常見的例子

冒泡階段的應用​

冒泡階段特別適合用於以下場景:

1. 父元素統一監聽子元素的事件

這種模式常被稱為 事件委派(Event Delegation)。它能夠有效減少事件監聽器的數量,並支持動態新增或刪除子元素。

範例:列表項的點擊事件

假設我們有一個動態生成的列表,每個項目都需要響應點擊事件。可以在父層

    上添加事件監聽器,通過 event.target 獲取具體被點擊的項目。

    function handleItemClick(event) { console.log('點擊的項目是:', event.target.textContent);}

    • 項目 1
    • 項目 2
    • 項目 3

    2. 全局事件監聽

    在某些情況下,我們需要監聽整個頁面中的某類事件。例如,只在點擊彈窗以外的頁面空白處時才隱藏彈窗。

    範例:點擊空白處關閉彈窗

    function Modal({ onClose }) { function handleOutsideClick(event) { if (event.target.id === 'modal-background') { onClose(); // 僅在點擊背景時觸發 } } return (

    );}

    捕獲階段的應用​

    捕獲階段適合處理需要「優先攔截」的場景,讓特定邏輯早於其他事件執行。

    1. 攔截全局事件

    捕獲階段適合在事件到達目標元素之前攔截並處理邏輯。例如,全局鍵盤監聽。

    範例:按下 Escape 鍵關閉彈窗

    useEffect(() => { function handleKeyDown(event) { if (event.key === 'Escape') { closeModal(); // 關閉彈窗 } } document.addEventListener('keydown', handleKeyDown, true); // 捕獲階段 return () => document.removeEventListener('keydown', handleKeyDown, true);}, []);

    分析:

    true 表示在捕獲階段執行監聽邏輯。

    適合需要優先執行的邏輯,例如攔截鍵盤操作。

    2. 特定元素優先處理

    有時,我們希望某些元素的事件能夠在目標元素邏輯執行前處理。例如,記錄用戶行為的追蹤工具,可以在捕獲階段蒐集事件資料。

    範例:用戶行為追蹤

    function handleCapture(event) { console.log('事件捕獲:', event.type, event.target);}

    如何阻止事件傳遞?​

    現在我們已經知道當事件觸發時,會根據事件模型的規則在捕獲階段和冒泡階段進行傳遞。但在某些情況下,我們可能希望事件在特定的邏輯處理後就終止傳播,避免影響其他元素的行為。JavaScript 提供了 event.stopPropagation() 方法來實現這一目標。

    以下我們來看幾個實際的應用場景:

    1. 防止父層事件被觸發

    有時,我們希望某個子元素處理事件後,阻止該事件繼續冒泡到父層。例如,在一個模態框內的按鈕點擊時,不應觸發背景的關閉邏輯。

    範例:點擊按鈕時不關閉模態框

    function Modal({ onClose }) { function handleBackgroundClick() { onClose(); } function handleButtonClick(event) { event.stopPropagation(); // 阻止事件冒泡到背景 console.log('按鈕被點擊'); } return (

    這是一個模態框

    );}

    當點擊背景時,觸發 handleBackgroundClick 關閉模態框。

    當點擊按鈕時,handleButtonClick 使用 event.stopPropagation(),阻止事件冒泡到背景。

    2. 停止全局事件的影響

    當頁面設置了全局事件監聽器時,內層的某些互動不應觸發全局邏輯。例如,一個嵌套表單的提交按鈕,不應觸發父層監聽的 click 事件。

    範例:嵌套按鈕的行為控制

    function App() { function handleGlobalClick() { console.log('全局點擊事件'); } function handleInnerClick(event) { event.stopPropagation(); // 阻止冒泡 console.log('內層按鈕事件'); } return (

    外層點擊區域

    );}

    點擊按鈕時,僅觸發 handleInnerClick。

    點擊外層區域時,觸發 handleGlobalClick。

    如何阻止瀏覽器的預設行為?​

    在上一節中,我們學到了如何使用 event.stopPropagation() 阻止事件的傳遞,避免某些不必要的邏輯被觸發。然而,事件的影響不僅限於傳播,還涉及到瀏覽器的預設行為。

    在許多互動場景中,瀏覽器會對某些事件觸發一系列預設行為。例如:

    表單事件: 當表單被提交時,瀏覽器會嘗試重新載入頁面。

    超連結點擊: 點擊 元素時,瀏覽器會跳轉到 href 指定的目標地址。

    鍵盤事件: 按下 Enter 鍵時,可能會觸發表單提交。

    當這些預設行為不符合我們的需求時,可以使用 event.preventDefault() 方法,它不會影響事件的傳遞,但會阻止瀏覽器執行與該事件相關的預設操作。

    以下我們來看幾個應用場景:

    1. 避免表單提交刷新頁面

    在表單處理時,我們常常需要阻止瀏覽器的預設提交行為,以便執行自定義邏輯(例如 Ajax 提交)。

    範例:表單提交邏輯

    function handleSubmit(event) { event.preventDefault(); // 阻止預設的表單提交行為 alert('表單提交已被攔截'); // 在這裡執行自定義提交邏輯}

    2. 防止超連結跳轉

    當我們希望使用超連結執行某些自定義操作(如切換視圖或觸發動作)而非跳轉時,可以阻止它的預設跳轉行為。

    範例:超連結作為導航按鈕

    function handleClick(event) { event.preventDefault(); // 阻止跳轉 console.log('導航至自定義路由邏輯');} 點擊我

    event.preventDefault() 阻止了瀏覽器跳轉到 /example。

    可以在 handleClick 中執行自定義的導航邏輯(例如 React Router 的 useNavigate)。

    3. 攔截鍵盤快捷鍵

    有時,我們需要攔截某些鍵盤快捷鍵的預設行為,例如阻止 F5 刷新或 Ctrl+S 儲存頁面。

    範例:自定義快捷鍵

    useEffect(() => { function handleKeyDown(event) { if (event.key === 'F5') { event.preventDefault(); // 阻止頁面刷新 console.log('自定義刷新行為'); } else if (event.ctrlKey && event.key === 's') { event.preventDefault(); // 阻止瀏覽器儲存 console.log('儲存資料到伺服器'); } } window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown);}, []);

    攔截了 F5 和 Ctrl+S 的預設行為。

    提供了自定義的行為(如自動儲存資料或避免刷新)。

    Reference​

    React 官方文件:Responding to Events

    MDN:JavaScript 事件的捕獲與冒泡

更多尼泊尔内容

1000人民币等于多少日元
世界杯365网站打不开

1000人民币等于多少日元

🗓️ 08-09 👁️ 2511
新浪微博如何发布新微博
office365邮箱手机版

新浪微博如何发布新微博

🗓️ 09-23 👁️ 835
2025年度好玩手指游戏排行榜创意无限乐趣无穷指尖游戏大集合
世界杯365网站打不开

2025年度好玩手指游戏排行榜创意无限乐趣无穷指尖游戏大集合

🗓️ 09-02 👁️ 6730
景甜个人资料简介
bet28365365官网

景甜个人资料简介

🗓️ 07-16 👁️ 5672
阴阳师退寮多久才能进
office365邮箱手机版

阴阳师退寮多久才能进

🗓️ 10-26 👁️ 735
word背景颜色怎么设置绿色?把word背景调成绿色
世界杯365网站打不开

word背景颜色怎么设置绿色?把word背景调成绿色

🗓️ 07-15 👁️ 2403
手机查公交车软件排行榜TOP10推荐
office365邮箱手机版

手机查公交车软件排行榜TOP10推荐

🗓️ 10-30 👁️ 3226
安卓手机如何下载Instagram应用?
bet28365365官网

安卓手机如何下载Instagram应用?

🗓️ 07-25 👁️ 3189
《七龙珠》:为什么弗利萨前期可以轻易毁灭星球后期就不行了
世界杯365网站打不开

《七龙珠》:为什么弗利萨前期可以轻易毁灭星球后期就不行了

🗓️ 10-23 👁️ 3624