自定义事件实现

使用 CustomEvent 创建和触发原生自定义事件

问题

如何使用原生 JavaScript 创建和触发自定义事件?

解答

基本用法

// 1. 创建自定义事件
const myEvent = new CustomEvent('myEvent', {
  detail: { message: 'Hello World', id: 123 }, // 自定义数据
  bubbles: true,    // 是否冒泡
  cancelable: true  // 是否可取消
});

// 2. 监听事件
document.addEventListener('myEvent', (e) => {
  console.log('收到事件:', e.detail); // { message: 'Hello World', id: 123 }
});

// 3. 触发事件
document.dispatchEvent(myEvent);

实际应用示例

// 封装一个简单的事件总线
const EventBus = {
  // 触发事件
  emit(eventName, data) {
    const event = new CustomEvent(eventName, {
      detail: data,
      bubbles: false
    });
    document.dispatchEvent(event);
  },

  // 监听事件
  on(eventName, callback) {
    const handler = (e) => callback(e.detail);
    document.addEventListener(eventName, handler);
    // 返回取消监听的函数
    return () => document.removeEventListener(eventName, handler);
  },

  // 只监听一次
  once(eventName, callback) {
    const handler = (e) => callback(e.detail);
    document.addEventListener(eventName, handler, { once: true });
  }
};

// 使用
const unsubscribe = EventBus.on('user:login', (user) => {
  console.log('用户登录:', user.name);
});

EventBus.emit('user:login', { name: '张三', id: 1 });

// 取消监听
unsubscribe();

在 DOM 元素上使用

const button = document.querySelector('#myButton');

// 监听自定义事件
button.addEventListener('customClick', (e) => {
  console.log('自定义点击:', e.detail.count);
});

// 触发自定义事件
let count = 0;
button.addEventListener('click', () => {
  count++;
  button.dispatchEvent(new CustomEvent('customClick', {
    detail: { count },
    bubbles: true // 允许冒泡,父元素也能监听到
  }));
});

旧版浏览器兼容写法

// 使用 Event 构造函数(不支持 detail)
const event = new Event('build', { bubbles: true });

// 更老的写法(IE 兼容)
const oldEvent = document.createEvent('CustomEvent');
oldEvent.initCustomEvent('myEvent', true, true, { message: 'data' });
document.dispatchEvent(oldEvent);

关键点

  • CustomEvent 构造函数创建自定义事件,detail 属性传递数据
  • dispatchEvent() 触发事件,addEventListener() 监听事件
  • bubbles: true 允许事件冒泡,父元素可以捕获子元素的事件
  • cancelable: true 配合 e.preventDefault() 可以取消事件默认行为
  • 自定义事件是同步执行的,dispatchEvent 会等待所有监听器执行完毕