원클릭으로
event
Event system for Web Components including emit() and custom event classes. Invoke when creating or updating component events.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Event system for Web Components including emit() and custom event classes. Invoke when creating or updating component events.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
@attribute decorator for defining HTML attribute-mapped properties. Invoke when creating or updating component attributes that map to HTML attributes.
@property decorator for defining pure JavaScript properties that do NOT map to HTML attributes. Invoke when creating internal component state properties.
BEM SCSS mixins for component styling. Invoke when writing or updating component SCSS styles using block/element/modifier/state mixins.
Component CSS styling standards including variables, host styles, states, and responsive patterns. Invoke when creating or updating component SCSS files.
@CustomElement decorator for registering Web Components. Invoke when creating new components or modifying component registration options.
Component documentation generation. Invoke when writing or updating component documentation including API tables, examples, and usage guides.
| name | event |
| description | Event system for Web Components including emit() and custom event classes. Invoke when creating or updating component events. |
组件事件的派发规范,包括自定义事件类(对外公开)和 emit()(内部通信)两种模式。
| 场景 | 模式 | 理由 |
|---|---|---|
| 对外公开事件(用户监听的) | 自定义事件类 | 控制台显示类名(如 EaCheckboxChangeEvent),类型安全 |
| 父子组件内部通信 | this.emit() | 轻量级,无需定义事件类 |
为什么自定义事件类用于对外公开事件?
在浏览器控制台中:
dispatchEvent(new EaCheckboxChangeEvent(...)) → 显示为 EaCheckboxChangeEvent { detail: { value, checked } }this.emit("change", { detail: {...} }) → 显示为 CustomEvent { type: "change", detail: {...} }自定义事件类提供了更好的调试体验和类型安全。
this.emit() 仅用于父子组件内部通信,事件名统一使用 ea-{component}-{event} 格式标识其内部性质。
说明:此类事件主要服务于组件内部逻辑,即便用户监听也不会获得实质性业务价值。
// ea-tab 向 ea-tabs 通知关闭按钮点击
this.emit("ea-tab-close-icon-click", { detail: { name: this.name } });
// ea-sub-menu 向 ea-menu 通知点击
this.emit("ea-sub-menu-click", { detail: { index, item } });
// ea-dropdown-item 向 ea-dropdown 通知点击
this.emit("ea-dropdown-item-click", { detail: { value } });
emit(eventName: string, options?: CustomEventInit): boolean
默认配置:bubbles: true, composed: true, cancelable: false
this.emit() 的事件名统一使用 ea- 前缀,格式为 ea-{component}-{event}:
// ✅ 正确:ea-{component}-{event} 格式标识内部通信
this.emit("ea-tab-close-icon-click", { detail: { name } });
this.emit("ea-sub-menu-click", { detail: { index, item } });
// ❌ 错误:无组件名前缀,不够明确
this.emit("ea-close-icon-click", { detail: { name } });
// ❌ 错误:无前缀,外部用户可能误监听
this.emit("close-icon-click", { detail: { name } });
this.emit("click", { detail: { index, item } });
对于对外公开的事件(用户监听的),创建自定义事件类。
自定义事件类的事件名应优先使用原生事件名,仅在没有原生对应事件时使用 ea- 前缀:
| 事件类别 | 命名策略 | 示例 |
|---|---|---|
| 原生 DOM 事件(focus, blur, change, input 等) | 使用原生事件名 | focus, blur, change, input |
| 组件特有事件(无原生对应) | 使用 ea- 前缀 | ea-clear, ea-sort-change, ea-visible-change |
为什么原生事件名优先?
focus、blur、change、input 等原生事件名原生事件名使用注意事项:
e.stopPropagation() 阻止原生事件穿透 Shadow DOM,然后从宿主元素派发自定义事件,防止用户收到两次同名事件"focus"、"change")已在内置类型中定义,重复注册会导致类型冲突。仅 ea- 前缀的事件名注册到 GlobalEventHandlersEventMap// ✅ 正确:原生事件名 + stopPropagation
this._originalWrapper.addEventListener("input", (e: Event) => {
e.stopPropagation(); // 阻止原生事件穿透 Shadow DOM
this._handleInput(e);
}, { signal });
// 从宿主元素派发自定义事件
this.dispatchEvent(new EaInputInputEvent({ value }));
// ✅ 正确:组件特有事件使用 ea- 前缀
this.dispatchEvent(new EaInputClearEvent({ oldValue }));
// ❌ 错误:原生事件使用 ea- 前缀
this.dispatchEvent(new EaInputFocusEvent()); // super("ea-focus", ...) ❌
// 应改为 super("focus", ...)
ea-component/
├── index.ts
├── index.scss
├── types.d.ts
└── events/
├── EaComponentChangeEvent.ts
└── EaComponentSelectEvent.ts
// events/EaInputChangeEvent.ts
export interface EaInputChangeEventDetail {
value: string;
}
export class EaInputChangeEvent extends Event {
readonly detail: EaInputChangeEventDetail;
constructor(detail: EaInputChangeEventDetail) {
super("change", { bubbles: true, composed: true });
this.detail = detail;
}
}
// 注意:原生事件名不注册到 GlobalEventHandlersEventMap(会与内置类型冲突)
// events/EaInputClearEvent.ts
export interface EaInputClearEventDetail {
oldValue: string;
}
export class EaInputClearEvent extends Event {
readonly detail: EaInputClearEventDetail;
constructor(detail: EaInputClearEventDetail) {
super("ea-clear", { bubbles: true, composed: true });
this.detail = detail;
}
}
declare global {
interface GlobalEventHandlersEventMap {
"ea-clear": EaInputClearEvent;
}
}
ea- 前缀export interface,命名为 Ea{Component}{Action}EventDetailreadonly 修饰符,确保不可变{ bubbles: true, composed: true };需要阻止默认行为时添加 cancelable: trueea- 前缀的事件名注册到 GlobalEventHandlersEventMap,原生事件名不注册(避免与内置类型冲突)Ea{Component}{Action}Event(如 EaTableSortChangeEvent、EaTreeCheckEvent)stopPropagation() 阻止原生事件穿透import { EaInputChangeEvent } from "./events/EaInputChangeEvent";
import { EaInputClearEvent } from "./events/EaInputClearEvent";
// 原生事件名
this.dispatchEvent(new EaInputChangeEvent({ value: "new" }));
// 组件特有事件
this.dispatchEvent(new EaInputClearEvent({ oldValue: "" }));
import { EaInputChangeEvent } from "@components/ea-input/events/EaInputChangeEvent";
import { EaInputClearEvent } from "@components/ea-input/events/EaInputClearEvent";
// 原生事件名 - 直接使用字符串
component.addEventListener("change", (e: EaInputChangeEvent) => {
console.log(e.detail.value);
});
// 组件特有事件 - 使用 ea- 前缀
component.addEventListener("ea-clear", (e: EaInputClearEvent) => {
console.log(e.detail.oldValue);
});
| 事件名 | 用途 | 典型组件 |
|---|---|---|
focus | 获得焦点 | input、textarea、select |
blur | 失去焦点 | input、textarea、select |
change | 值提交变化 | input、select、checkbox、switch |
input | 实时输入 | input、textarea |
ea- 前缀)| 事件名 | 用途 | 典型组件 |
|---|---|---|
ea-clear | 清除操作 | input、select、color-picker |
ea-sort-change | 排序变化 | table |
ea-current-change | 当前项变化 | table、tree、pagination |
ea-check / ea-check-change | 勾选变化 | tree |
ea-remove-tag | 移除标签 | select、tag |
ea-visible-change | 下拉框显隐变化 | select、picker |
ea-select / ea-select-all | 选中变化 | table |
ea-row-click / ea-cell-click | 行/单元格点击 | table |
ea-close | 关闭 | alert、tour |
| 事件名 | 用途 | 通信方向 |
|---|---|---|
ea-tab-close-icon-click | 关闭按钮点击 | ea-tab → ea-tabs |
ea-sub-menu-click | 子菜单点击 | ea-sub-menu → ea-menu |
ea-dropdown-item-click | 下拉项点击 | ea-dropdown-item → ea-dropdown |
ea-transfer-panel-select-change | 面板选中变化 | panel → ea-transfer |
ea-table-column-change | 列属性变化 | ea-table-column → ea-table |
ea-descriptions-item-change | 描述项属性变化 | ea-descriptions-item → ea-descriptions |
collapse-item-click | 折叠项点击 | ea-collapse-item → ea-collapse |
// events/EaSliderChangeEvent.ts
export class EaSliderChangeEvent extends Event {
readonly detail: EaSliderChangeEventDetail;
constructor(detail: EaSliderChangeEventDetail) {
super("change", { bubbles: true, cancelable: true, composed: true });
this.detail = detail;
}
}
// 使用时可通过 preventDefault() 阻止
const event = new EaSliderChangeEvent({ value: 50 });
if (!this.dispatchEvent(event)) {
return;
}
当组件封装了原生表单元素(如 input、textarea)时,需要阻止原生事件穿透 Shadow DOM,然后从宿主元素重新派发自定义事件:
private _bindOriginalEvents(): void {
const { signal } = this._originalAbortController;
// 阻止原生 input 事件穿透,派发自定义事件
this._originalWrapper.addEventListener("input", (e: Event) => {
const target = e.target as HTMLElement;
if (!target.classList.contains(bem.e("original"))) return;
e.stopPropagation(); // 关键:阻止原生事件穿透 Shadow DOM
this._handleInput(e);
}, { signal });
this._originalWrapper.addEventListener("focusin", (e: FocusEvent) => {
const target = e.target as HTMLElement;
if (!target.classList.contains(bem.e("original"))) return;
e.stopPropagation();
this._handleFocus();
}, { signal });
}
private _handleInput(e: Event): void {
const { value } = e.target as HTMLInputElement;
this.value = value;
this.dispatchEvent(new EaInputInputEvent({ value }));
}
private _handleFocus(): void {
this._isFocus = true;
this.updateContainerClasslist();
this.dispatchEvent(new EaInputFocusEvent());
}
// ea-tab 内部
this.emit("ea-tab-close-icon-click", { detail: { name: this.name } });
// ea-tabs 监听
@listen("ea-tab-close-icon-click")
private _handleTabClose(e: CustomEvent) {
this.dispatchEvent(new EaTabRemoveEvent({ name: e.detail.name }));
}
命令式创建的组件(message、notification、message-box)在实例工具类中派发事件:
// EaMessageInstance.ts
el.emit("ea-message-show");
el.emit("ea-message-shown");
el.emit("ea-message-hide");
el.emit("ea-message-hidden");
el.emit("ea-message-close");