CustomEvent
概述
CustomEvent
是 ECMAScript 标准提供的原生 API,用于创建和触发自定义事件。它允许开发者定义任意名称的事件类型(如 my-click
、data-loaded
),并传递自定义数据,实现组件间通信或复杂交互逻辑。
语法与核心方法
1. 创建自定义事件
通过 new CustomEvent()
构造函数创建事件对象:
const event = new CustomEvent('eventName', {
detail: { /* 自定义数据 */ },
bubbles: true, // 是否冒泡
cancelable: true, // 是否可取消
composed: true // 是否穿透 Shadow DOM
});
2. 触发事件
在元素或 window
对象上调用 dispatchEvent()
:
element.dispatchEvent(event);
3. 监听事件
使用 addEventListener()
或 on
属性绑定处理函数:
element.addEventListener('eventName', (e) => {
console.log(e.detail); // 获取自定义数据
});
关键属性详解
属性 | 类型 | 说明 |
---|---|---|
detail | Object | 自定义数据容器(必选),可通过 event.detail.key = value 修改。 |
bubbles | boolean | 默认 false ,设为 true 后事件会向父元素冒泡。 |
cancelable | boolean | 默认 false ,设为 true 后可通过 e.preventDefault() 阻止默认行为。 |
composed | boolean | 默认 false ,设为 true 后事件可穿透 Shadow DOM(仅限跨宿主传播)。 |
场景示例
示例 1:基础用法
<div id="btn">点击我</div>
<div id="log"></div>
<script>
// 定义按钮点击事件
const btn = document.getElementById('btn');
btn.addEventListener('click', () => {
// 创建并触发自定义事件,携带数据
const event = new CustomEvent('custom-btn-click', {
detail: {
timestamp: new Date().toISOString(),
message: '按钮被点击了!'
}
});
btn.dispatchEvent(event);
});
// 监听自定义事件
document.addEventListener('custom-btn-click', (e) => {
const log = document.getElementById('log');
log.innerHTML = ``;
});
</script>
效果:点击按钮后,在页面显示点击时间和消息。
示例 2:事件冒泡与阻止默认行为
<div id="parent">
<button id="child">子按钮</button>
</div>
<script>
// 子元素触发带冒泡的自定义事件
document.getElementById('child').addEventListener('click', () => {
const event = new CustomEvent('child-event', {
bubbles: true,
cancelable: true,
detail: { text: '来自子元素' }
});
// 触发事件前检查是否被取消
if (!event.defaultPrevented) {
this.dispatchEvent(event);
}
});
// 父元素监听并阻止冒泡
document.getElementById('parent').addEventListener('child-event', (e) => {
console.log('父元素收到事件:', e.detail.text);
e.preventDefault(); // 阻止进一步冒泡
});
// 全局监听(不会触发,因已被父元素阻止)
window.addEventListener('child-event', () => {
console.log('全局监听未触发');
});
</script>
输出:
父元素收到事件: 来自子元素
示例 3:跨 Shadow DOM 通信
<template id="shadow-template">
<style>
.shadow-box { background: #eee; padding: 10px; }
</style>
<div class="shadow-box" id="shadow-content">
Shadow DOM 内容
</div>
</template>
<div id="host"></div>
<script>
// 创建 Shadow DOM
const host = document.getElementById('host');
const shadowRoot = host.attachShadow({ mode: 'open' });
const template = document.getElementById('shadow-template');
shadowRoot.appendChild(template.content.cloneNode(true));
// 在 Shadow DOM 内触发自定义事件(设置 composed: true)
const shadowContent = shadowRoot.getElementById('shadow-content');
shadowContent.addEventListener('click', () => {
const event = new CustomEvent('shadow-message', {
composed: true, // 允许穿透到宿主
detail: { text: '来自 Shadow DOM' }
});
shadowContent.dispatchEvent(event);
});
// 宿主元素监听穿透事件
host.addEventListener('shadow-message', (e) => {
console.log('宿主收到 Shadow DOM 消息:', e.detail.text);
});
</script>
效果:点击 Shadow DOM 内容时,宿主元素的监听器会收到消息。
注意事项
事件名称规范
- 自定义事件名称应使用
小驼峰命名法
(如user-login
),与内置事件(如click
)区分。 - 避免使用浏览器保留名称(如
load
、error
),除非明确重写行为。
- 自定义事件名称应使用
兼容性处理
旧版浏览器(如 IE)需使用 polyfill:
javascript
if (!window.CustomEvent) {
window.CustomEvent = function (name, params) {
params = params || { bubbles: false, cancelable: false };
const event = document.createEvent('Event');
event.initEvent(name, params.bubbles, params.cancelable);
event.detail = params.detail;
return event;
};
}
性能优化
- 频繁触发事件可能导致性能问题,建议合理控制事件频率。
- 使用事件委托(Event Delegation)减少监听器数量。
应用场景
- 组件化开发:如 React/Vue 中通过自定义事件实现父子组件通信。
- 第三方库扩展:为现有库(如 lodash)添加插件式事件系统。
- 复杂交互逻辑:如表单多步骤验证、动画状态同步。
通过 CustomEvent
,开发者可以构建更灵活、可维护的代码结构,实现与非侵入式的组件交互。