事件系统
内联闭包事件
在 html! 宏中直接使用闭包处理事件,闭包签名为 FnMut(Event),其中 Event 是 web_sys::Event:
html! {
button {
onclick: move |event: Event| {
// 处理点击
}
"Click me"
}
}NativeEventHandler 事件
通过 NativeEventHandler::create 创建事件处理器,适合在组件间传递或复用:
pub fn counter_on_increment(counter: Signal<i32>) -> NativeEventHandler {
NativeEventHandler::create(NativeEventName::Click, move |_event: Event| {
let current: i32 = counter.get();
counter.set(current + 1);
})
}
html! {
button {
onclick: counter_on_increment(count)
"Increment"
}
}提示
NativeEventHandler::create 接受两个参数:事件名称(NativeEventName 枚举)和事件处理闭包。闭包签名为 FnMut(Event)。
输入事件
通过 event.dyn_ref::<HtmlInputElement>() 获取具体的 DOM 元素,再读取其值:
html! {
input {
r#type: "text"
placeholder: "Enter text"
oninput: move |event: Event| {
if let Some(target) = event.target()
&& let Ok(input) = target.clone().dyn_into::<HtmlInputElement>() {
name_signal.set(input.value());
}
}
}
}表单变更事件
html! {
input {
r#type: "checkbox"
checked: agree_signal
onchange: move |event: Event| {
if let Some(target) = event.target()
&& let Ok(input) = target.clone().dyn_into::<HtmlInputElement>() {
agree_signal.set(input.checked());
}
}
}
}支持的事件名称
| 类别 | 事件名称 |
|---|---|
| 鼠标 | onclick, ondblclick, onmousedown, onmouseup, onmousemove, onmouseenter, onmouseleave, onmouseover, onmouseout, oncontextmenu |
| 输入 | oninput |
| 键盘 | onkeydown, onkeyup, onkeypress |
| 焦点 | onfocus, onblur, onfocusin, onfocusout |
| 表单 | onsubmit, onchange |
| 拖拽 | ondrag, ondragstart, ondragend, ondragover, ondragenter, ondragleave, ondrop |
| 触摸 | ontouchstart, ontouchend, ontouchmove, ontouchcancel |
| 滚轮 | onwheel |
| 剪贴板 | oncopy, oncut, onpaste |
| 媒体 | onplay, onpause, onended, onloadeddata, oncanplay, onvolumechange, ontimeupdate |
| 路由 | onhashchange |
| 窗口 | onresize, onscroll, onload, onunload, onbeforeunload, onerror, ononline, onoffline, onvisibilitychange |
| 动画 | onanimationstart, onanimationend, onanimationiteration |
| 过渡 | ontransitionstart, ontransitionend, ontransitionrun |
提示
框架对常用事件(如 click、input、change 等)使用事件委托(event delegation),在根元素上统一监听,通过 data-euv-id 属性分发到对应的事件处理器,减少 DOM 事件监听器数量。不在委托列表中的事件会自动在对应元素上单独绑定。
事件数据
事件回调接收 web_sys::Event 类型参数,通过 dyn_ref::<T>() 下转型为具体的事件类型来获取数据:
move |event: Event| {
if let Some(mouse_event) = event.dyn_ref::<MouseEvent>() {
let x: i32 = mouse_event.client_x();
let y: i32 = mouse_event.client_y();
let button: i16 = mouse_event.button();
let ctrl_key: bool = mouse_event.ctrl_key();
let shift_key: bool = mouse_event.shift_key();
let alt_key: bool = mouse_event.alt_key();
let meta_key: bool = mouse_event.meta_key();
}
if let Some(keyboard_event) = event.dyn_ref::<KeyboardEvent>() {
let key: String = keyboard_event.key();
let code: String = keyboard_event.code();
let repeat: bool = keyboard_event.repeat();
}
if let Some(target) = event.target()
&& let Ok(input) = target.clone().dyn_into::<HtmlInputElement>() {
let value: String = input.value();
let checked: bool = input.checked();
}
if let Some(drag_event) = event.dyn_ref::<DragEvent>() {
let client_x: i32 = drag_event.client_x();
let client_y: i32 = drag_event.client_y();
}
if let Some(touch_event) = event.dyn_ref::<TouchEvent>() {
let touches: TouchList = touch_event.touches();
let count: u32 = touches.length();
}
if let Some(wheel_event) = event.dyn_ref::<WheelEvent>() {
let delta_x: f64 = wheel_event.delta_x();
let delta_y: f64 = wheel_event.delta_y();
let delta_mode: u32 = wheel_event.delta_mode();
}
if let Some(clipboard_event) = event.dyn_ref::<ClipboardEvent>() {
let data: Option<String> = clipboard_event
.clipboard_data()
.and_then(|cd| cd.get_data("text").ok());
}
}提示
事件名在 HTML 宏中使用 on 前缀小写形式,如 onclick、oninput,框架自动映射到对应的事件类型。
提示
事件回调接收的是 web_sys::Event,需通过 event.dyn_ref::<T>() 下转型为具体事件类型(如 MouseEvent、KeyboardEvent),或通过 event.target() 获取 DOM 元素后 dyn_into::<HtmlInputElement>() 等方式读取值。dyn_ref 和 dyn_into 来自 wasm_bindgen::JsCast trait,已在 euv 中重新导出。
常用事件处理器工具函数
框架示例中提供了一些通用的事件处理器创建函数,可在项目中复用:
// 切换布尔信号
pub fn use_toggle(signal: Signal<bool>) -> NativeEventHandler {
NativeEventHandler::create(NativeEventName::Click, move |_event: Event| {
let current: bool = signal.get();
signal.set(!current);
})
}
// 输入值绑定到信号(支持 input、textarea、select)
pub fn on_input_value(signal: Signal<String>) -> NativeEventHandler {
NativeEventHandler::create(NativeEventName::Input, move |event: Event| {
let value: Option<String> = event.target().and_then(|target: EventTarget| {
if let Ok(input) = target.clone().dyn_into::<HtmlInputElement>() {
return Some(input.value());
}
if let Ok(textarea) = target.clone().dyn_into::<HtmlTextAreaElement>() {
return Some(textarea.value());
}
if let Ok(select) = target.clone().dyn_into::<HtmlSelectElement>() {
return Some(select.value());
}
None
});
if let Some(value) = value {
signal.set(value);
}
})
}
// 变更值绑定到信号(支持 input、select、textarea)
pub fn on_change_value(signal: Signal<String>) -> NativeEventHandler {
NativeEventHandler::create(NativeEventName::Change, move |event: Event| {
let value: Option<String> = event.target().and_then(|target: EventTarget| {
if let Ok(input) = target.clone().dyn_into::<HtmlInputElement>() {
return Some(input.value());
}
if let Ok(select) = target.clone().dyn_into::<HtmlSelectElement>() {
return Some(select.value());
}
if let Ok(textarea) = target.clone().dyn_into::<HtmlTextAreaElement>() {
return Some(textarea.value());
}
None
});
if let Some(value) = value {
signal.set(value);
}
})
}
// Checkbox 变更绑定
pub fn on_change_checked(signal: Signal<bool>) -> NativeEventHandler {
NativeEventHandler::create(NativeEventName::Change, move |event: Event| {
if let Some(target) = event.target()
&& let Ok(input) = target.clone().dyn_into::<HtmlInputElement>() {
signal.set(input.checked());
}
})
}提示
on_input_value 会自动识别 HtmlInputElement、HtmlTextAreaElement、HtmlSelectElement 三种表单元素,无需为不同元素类型编写不同的处理器。on_change_value 同理,适用于需要 onchange 事件的场景。