【JS】512- JS 自定義事件如此簡單!
- 2020 年 2 月 26 日
- 筆記
在前端開發世界中,JavaScript 和 HTML 之間往往通過 事件 來實現交互。其中多數為內置事件,本文主要介紹 JS自定義事件概念和實現方式,並結合案例詳細分析自定義事件的原理、功能、應用及注意事項。
一、什麼是自定義事件
在日常開發中,我們習慣監聽頁面許多事件,諸如:點擊事件( click
)、鼠標移動事件( mousemove
)、元素失去焦點事件( blur
)等等。 事件本質是一種通信方式,是一種消息,只有在多對象多模塊時,才有可能需要使用事件進行通信。在多模塊化開發時,可以使用自定義事件進行模塊間通信。 當某些基礎事件無法滿足我們業務,就可以嘗試 自定義事件來解決。
二、實現方式介紹
目前實現自定義事件的兩種主要方式是 JS 原生的 Event()
構造函數和 CustomEvent()
構造函數來創建。
1. Event()
Event()
構造函數, 創建一個新的事件對象 Event
。
1.1 語法
let myEvent = new Event(typeArg, eventInit);
1.2 參數
typeArg
:DOMString
類型,表示創建事件的名稱; eventInit
:可選配置項,包括:

1.3 演示示例
// 創建一個支持冒泡且不能被取消的 pingan 事件 let myEvent = new Event("pingan", {"bubbles":true, "cancelable":false}); document.dispatchEvent(myEvent); // 事件可以在任何元素觸發,不僅僅是document testDOM.dispatchEvent(myEvent);
1.4 兼容性

圖片來源:https://caniuse.com/
2. CustomEvent()
CustomEvent()
構造函數, 創建一個新的事件對象 CustomEvent
。
2.1 語法
let myEvent = new CustomEvent(typeArg, eventInit);
2.2 參數
typeArg
:DOMString
類型,表示創建事件的名稱; eventInit
:可選配置項,包括:

2.3 演示示例
// 創建事件 let myEvent = new CustomEvent("pingan", { detail: { name: "wangpingan" } }); // 添加適當的事件監聽器 window.addEventListener("pingan", e => { alert(`pingan事件觸發,是 ${e.detail.name} 觸發。`); }); document.getElementById("leo2").addEventListener( "click", function () { // 派發事件 window.dispatchEvent(pingan2Event); } )

我們也可以給自定義事件添加屬性:
myEvent.age = 18;
2.4 兼容性

圖片來源:https://caniuse.com/
2.5 IE8 兼容
分發事件時,需要使用 dispatchEvent
事件觸發,它在 IE8 及以下版本中需要進行使用 fireEvent
方法兼容:
if(window.dispatchEvent) { window.dispatchEvent(myEvent); } else { window.fireEvent(myEvent); }
3. Event() 與 CustomEvent() 區別
從兩者支持的參數中,可以看出: Event()
適合創建簡單的自定義事件,而 CustomEvent()
支持參數傳遞的自定義事件,它支持 detail
參數,作為事件中需要被傳遞的數據,並在 EventListener
獲取。
注意: 當一個事件觸發時,若相應的元素及其上級元素沒有進行事件監聽,則不會有回調操作執行。 當需要對於子元素進行監聽,可以在其父元素進行事件託管,讓事件在事件冒泡階段被監聽器捕獲並執行。此時可以使用 event.target
獲取到具體觸發事件的元素。
三、使用場景
事件本質是一種消息,事件模式本質上是觀察者模式的實現,即能用觀察者模式的地方,自然也能用事件模式。
1.場景介紹
比如這兩種場景:
- 場景1:單個目標對象發生改變,需要通知多個觀察者一同改變。
如:當微博列表中點擊「關注」,此時會同時發生很多事:推薦更多類似微博,個人關注數增加…

- 場景2:解耦多模塊開協作。
如:小王負責A模塊開發,小陳負責B模塊開發,模塊B需要模塊A正常運行之後才能執行。
2. 代碼實現
2.1 場景1實現
場景1:單個目標對象發生改變,需要通知多個觀察者一同改變。 本例子模擬三個頁面進行演示: 1.微博列表頁(Weibo.js) 2.粉絲列表頁(User.js) 3.微博首頁(Home.js)
在微博列表頁(Weibo.js)中,我們導入其他兩個頁面,並且監聽【關注微博】按鈕的點擊事件,在回調事件中,創建一個自定義事件 focusUser
,並在 document
上使用 dispatchEvent
方法派發自定義事件。
// Weibo.js import UserModule from "./User.js"; import HomeModule from "./Home.js"; const eventButton = document.getElementById("eventButton"); eventButton.addEventListener("click", event => { const focusUser = new Event("focusUser"); document.dispatchEvent(focusUser); })
接下來兩個頁面實現的代碼基本一致,這裡為了方便觀察,設置了兩者不同輸出日誌。
// User.js const eventButton = document.getElementById("eventButton"); document.addEventListener("focusUser", event => { console.log("【粉絲列表頁】監聽到自定義事件觸發,event:",event); }) // Home.js const eventButton = document.getElementById("eventButton"); document.addEventListener("focusUser", event => { console.log("【微博首頁】監聽到自定義事件觸發,event:",event); })
點擊【關注微博】按鈕後,看到控制台輸出如下日誌信息:

image.png
最終實現了,在微博列表頁(Weibo.js)組件負責派發事件,其他組價負責監聽事件,這樣三個組件之間耦合度非常低,完全不用關係對方,互相不影響。 其實這也是實現了觀察者模式。
2.2 場景2實現
場景2:解耦多模塊開協作。 舉個更直觀的例子,當微博需要加入【一鍵三連】新功能,需要產品原型和UI設計完後,程序員才能開發。 本例子模擬四個模塊: 1.流程控制(Index.js) 2.產品設計(Production.js) 3.UI設計(Design.js) 4.程序員開發(Develop.js)

image.png
在流程控制(Index.js)模塊中,我們需要將其他三個流程的模塊都導入進來,然後監聽【開始任務】按鈕的點擊事件,在回調事件中,創建一個自定義事件 startTask
,並在 document
上使用 dispatchEvent
方法派發自定義事件。
// Index.js import ProductionModule from "./Production.js"; import DesignModule from "./Design.js"; import DevelopModule from "./Develop.js"; const start = document.getElementById("start"); start.addEventListener("click", event => { console.log("開始執行任務") const startTask = new Event("startTask"); document.dispatchEvent(startTask); })
在 Production 產品設計模塊中,監聽任務開始事件 startTask
後,模擬1秒後原型設計完成,並派發一個新的事件 productionSuccess
,開始接下來的UI稿設計。
// Production.js document.addEventListener("startTask", () => { console.log("產品開始設計..."); setTimeout(() => { console.log("產品原型設計完成"); console.log("--------------"); document.dispatchEvent(new Event("productionSuccess")); }, 1000); });
在UI稿設計和程序開發模塊,其實也類似,代碼實現:
// Dedign.js document.addEventListener("productionSuccess", () => { console.log("UI稿開始設計..."); setTimeout(() => { console.log("UI稿設計完成"); console.log("--------------"); document.dispatchEvent(new Event("designSuccess")); }, 1000); }); // Production.js document.addEventListener("designSuccess", function (e) { console.log("開始開發功能..."); setTimeout(function () { console.log("【一鍵三連】開發完成"); }, 2000) });
開發完成後,我們點擊【開始任務】按鈕後,看到控制台輸出如下日誌信息:

image.png
最終實現了在 流程控制(Index.js)模塊負責派發事件,其他組件負責監聽事件,按流程完成其他任務。 可以看出,原型設計、UI稿設計和程序開發任務,互不影響,易於任務拓展。
四、總結
本文詳細介紹 JS自定義事件概念和實現方式,並結合兩個實際場景進行代碼演示。細心的小夥伴會發現,這兩個實際場景都是用 Event()
構造函數實現,當然也是可以使用 CustomEvent
構造函數來代替。 另外本文也詳細介紹兩種實現方式,包括其區別和兼容性。 最後也希望大家能在實際開發中,多思考代碼解耦,適當使用自定義事件來提高代碼質量。
五、參考文章
- 《javascript自定義事件功能與用法實例分析》
- 《Event – MDN》
- 《CustomEvent – MDN》