Events
Introduction
Yew integrates with the web-sys
crate and
uses the events from that crate. The table below lists all of the web-sys
events that are accepted in the html!
macro.
You can still add a Callback
for an event that is not listed in the table
below, see Manual event listener.
Event Types
All the event types mentioned in the following table are re-exported under yew::events
.
Using the types from yew::events
makes it easier to ensure version compatibility than
if you were to manually include web-sys
as a dependency in your crate because you won't
end up using a version which conflicts with the version that Yew specifies.
The event listener name is the expected name when adding an event Callback
in the html
macro:
use yew::{html, Callback};
html! {
<button onclick={Callback::from(|_| ())}>
// ^^^^^^^ event listener name
{ "Click me!" }
</button>
};
The event name is the listener without the "on" prefix, therefore, the onclick
event listener
listens for click
events.
Event listener name | web_sys Event Type |
---|---|
onabort | Event |
onauxclick | MouseEvent |
onblur | FocusEvent |
oncancel | Event |
oncanplay | Event |
oncanplaythrough | Event |
onchange | Event |
onclick | MouseEvent |
onclose | Event |
oncontextmenu | MouseEvent |
oncuechange | Event |
ondblclick | MouseEvent |
ondrag | DragEvent |
ondragend | DragEvent |
ondragenter | DragEvent |
ondragexit | DragEvent |
ondragleave | DragEvent |
ondragover | DragEvent |
ondragstart | DragEvent |
ondrop | DragEvent |
ondurationchange | Event |
onemptied | Event |
onended | Event |
onerror | Event |
onfocus | FocusEvent |
onfocusin | FocusEvent |
onfocusout | FocusEvent |
onformdata | Event |
oninput | InputEvent |
oninvalid | Event |
onkeydown | KeyboardEvent |
onkeypress | KeyboardEvent |
onkeyup | KeyboardEvent |
onload | Event |
onloadeddata | Event |
onloadedmetadata | Event |
onloadstart | ProgressEvent |
onmousedown | MouseEvent |
onmouseenter | MouseEvent |
onmouseleave | MouseEvent |
onmousemove | MouseEvent |
onmouseout | MouseEvent |
onmouseover | MouseEvent |
onmouseup | MouseEvent |
onpause | Event |
onplay | Event |
onplaying | Event |
onprogress | ProgressEvent |
onratechange | Event |
onreset | Event |
onresize | Event |
onscroll | Event |
onsecuritypolicyviolation | Event |
onseeked | Event |
onseeking | Event |
onselect | Event |
onslotchange | Event |
onstalled | Event |
onsubmit | FocusEvent |
onsuspend | Event |
ontimeupdate | Event |
ontoggle | Event |
onvolumechange | Event |
onwaiting | Event |
onwheel | WheelEvent |
oncopy | Event |
oncut | Event |
onpaste | Event |
onanimationcancel | AnimationEvent |
onanimationend | AnimationEvent |
onanimationiteration | AnimationEvent |
onanimationstart | AnimationEvent |
ongotpointercapture | PointerEvent |
onloadend | ProgressEvent |
onlostpointercapture | PointerEvent |
onpointercancel | PointerEvent |
onpointerdown | PointerEvent |
onpointerenter | PointerEvent |
onpointerleave | PointerEvent |
onpointerlockchange | Event |
onpointerlockerror | Event |
onpointermove | PointerEvent |
onpointerout | PointerEvent |
onpointerover | PointerEvent |
onpointerup | PointerEvent |
onselectionchange | Event |
onselectstart | Event |
onshow | Event |
ontouchcancel | TouchEvent |
ontouchend | TouchEvent |
ontouchmove | TouchEvent |
ontouchstart | TouchEvent |
ontransitioncancel | TransitionEvent |
ontransitionend | TransitionEvent |
ontransitionrun | TransitionEvent |
ontransitionstart | TransitionEvent |
Typed event target
In this section target (Event.target) is always referring to the element at which the event was dispatched from.
This will not always be the element at which the Callback
is placed.
In event Callback
s you may want to get the target of that event. For example, the
change
event gives no information but is used to notify that something has changed.
In Yew getting the target element in the correct type can be done in a few ways and we will go through
them here. Calling web_sys::Event::target
on an event returns an optional web_sys::EventTarget
type, which might not seem very useful when you want to know the value of your input element.
In all the approaches below we are going to tackle the same problem, so it's clear where the approach differs opposed to the problem at hand.
The Problem:
We have an onchange
Callback
on my <input>
element and each time it is invoked we want to send
an update Msg
to our component.
Our Msg
enum looks like this:
pub enum Msg {
InputValue(String),
}
Using JsCast
The wasm-bindgen
crate has
a useful trait; JsCast
which allows us to hop and skip our way to the type we want, as long as it implements JsCast
. We can
do this cautiously, which involves some runtime checks and failure types like Option
and Result
,
or we can do it dangerously.
Enough talk, more code:
[dependencies]
# need wasm-bindgen for JsCast
wasm-bindgen = "0.2"
use wasm_bindgen::JsCast;
use web_sys::{EventTarget, HtmlInputElement};
use yew::{
events::Event,
html,
Component, Context, Html,
};
pub struct Comp;
pub enum Msg {
InputValue(String),
}
impl Component for Comp {
type Message = Msg;
type Properties = ();
fn create(_: &Context<Self>) -> Self {
Self
}
fn view(&self, ctx: &Context<Self>) -> Html {
let link = ctx.link();
// Use batch_callback so if something unexpected happens we can return
// None and do nothing
let on_cautious_change = link.batch_callback(|e: Event| {
// When events are created the target is undefined, it's only
// when dispatched does the target get added.
let target: Option<EventTarget> = e.target();
// Events can bubble so this listener might catch events from child
// elements which are not of type HtmlInputElement
let input = target.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
input.map(|input| Msg::InputValue(input.value()))
});
let on_dangerous_change = link.callback(|e: Event| {
let target: EventTarget = e
.target()
.expect("Event should have a target when dispatched");
// You must KNOW target is a HtmlInputElement, otherwise
// the call to value would be Undefined Behaviour (UB).
Msg::InputValue(target.unchecked_into::<HtmlInputElement>().value())
});
html! {
<>
<label for="cautious-input">
{ "My cautious input:" }
<input onchange={on_cautious_change}
id="cautious-input"
type="text"
/>
</label>
<label for="dangerous-input">
{ "My dangerous input:" }
<input onchange={on_dangerous_change}
id="dangerous-input"
type="text"
/>
</label>
</>
}
}
}
Use batch_callback
when you want to conditionally return a value from a Callback
.
The methods from JsCast
are dyn_into
and unchecked_into
and you can probably see, they allowed
us to go from EventTarget
to HtmlInputElement
.
The dyn_into
method is cautious because at
runtime it will check whether the type is actually a HtmlInputElement
and if not return an
Err(JsValue)
, the JsValue
is a catch-all type and is essentially giving you back the object to try again.
At this point you might be thinking... when is the dangerous version ok to use? In the case above it
is safe1 as we've set the Callback
on to an element with no children so the target can
only be that same element.
1 As safe as anything can be when JS land is involved.
Using TargetCast
It is highly recommended to read Using JsCast first!
TargetCast
was designed to feel very similar to JsCast
- this is to allow new users to get a feel
for the behaviour of JsCast
but with the smaller scope of events and their targets.
TargetCast
vs JsCast
is purely preference, you will find that TargetCast
implements something
similar to what you would using JsCast
.
The TargetCast
trait is built on top of JsCast
and is specialized towards getting typed event
targets from events.
TargetCast
comes with Yew so no need to add a dependency in order to use the trait methods on events
but it works in a very similar way to JsCast
.
use web_sys::HtmlInputElement;
use yew::{
events::Event,
html,
// Need to import TargetCast
Component, Context, Html, TargetCast,
};
pub struct Comp;
pub enum Msg {
InputValue(String),
}
impl Component for Comp {
type Message = Msg;
type Properties = ();
fn create(_: &Context<Self>) -> Self {
Self
}
fn view(&self, ctx: &Context<Self>) -> Html {
let link = ctx.link();
let on_cautious_change = link.batch_callback(|e: Event| {
let input = e.target_dyn_into::<HtmlInputElement>();
input.map(|input| Msg::InputValue(input.value()))
});
let on_dangerous_change = link.callback(|e: Event| {
// You must KNOW target is a HtmlInputElement, otherwise
// the call to value would be Undefined Behaviour (UB).
Msg::InputValue(e.target_unchecked_into::<HtmlInputElement>().value())
});
html! {
<>
<label for="cautious-input">
{ "My cautious input:" }
<input onchange={on_cautious_change}
id="cautious-input"
type="text"
/>
</label>
<label for="dangerous-input">
{ "My dangerous input:" }
<input onchange={on_dangerous_change}
id="dangerous-input"
type="text"
/>
</label>
</>
}
}
}
If you followed the advice above and read about JsCast
, or know the trait, you can probably
see that TargetCast::target_dyn_into
feels similar to JsCast::dyn_into
but specifically
does the cast on the target of the event. TargetCast::target_unchecked_into
is similar to
JsCast::unchecked_into
, and as such all the same warnings above JsCast
apply to TargetCast
.
Using NodeRef
NodeRef
can be used instead of querying the event given to a Callback
.
use web_sys::HtmlInputElement;
use yew::{html, Component, Context, Html, NodeRef};
pub struct Comp {
my_input: NodeRef,
}
pub enum Msg {
InputValue(String),
}
impl Component for Comp {
type Message = Msg;
type Properties = ();
fn create(_: &Context<Self>) -> Self {
Self {
my_input: NodeRef::default(),
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
let link = ctx.link();
let my_input_ref = self.my_input.clone();
let onchange = link.batch_callback(move |_| {
let input = my_input_ref.cast::<HtmlInputElement>();
input.map(|input| Msg::InputValue(input.value()))
});
html! {
<>
<label for="my-input">
{ "My input:" }
<input ref={self.my_input.clone()}
{onchange}
id="my-input"
type="text"
/>
</label>
</>
}
}
}
Using NodeRef
, you can ignore the event and use the NodeRef::cast
method to get an
Option<HtmlInputElement>
- this is optional as calling cast
before the NodeRef
has been
set, or when the type doesn't match will return None
.
You might also see by using NodeRef
we don't have to send the String
back in the
Msg::InputValue
as we always have my_input
in the component state - so we could do the following:
use web_sys::HtmlInputElement;
use yew::{html, Component, Context, Html, NodeRef};
pub struct Comp {
my_input: NodeRef,
}
pub enum Msg {
// Signal the input element has changed
InputChanged,
}
impl Component for Comp {
type Message = Msg;
type Properties = ();
fn create(_: &Context<Self>) -> Self {
Self {
my_input: NodeRef::default(),
}
}
fn update(&mut self, _: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::InputChanged => {
if let Some(input) = self.my_input.cast::<HtmlInputElement>() {
let value = input.value();
// do something with value
true
} else {
false
}
}
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
let link = ctx.link();
let onchange = link.callback(|_| Msg::InputChanged);
html! {
<label for="my-input">
{ "My input:" }
<input ref={self.my_input.clone()}
{onchange}
id="my-input"
type="text"
/>
</label>
}
}
}
Which approach you take depends on your component and your preferences, there is no blessed way per se.
Manual event listener
You may want to listen to an event that is not supported by Yew's html
macro, see the
supported events listed here.
In order to add an event listener to one of elements manually we need the help of
NodeRef
so that in the rendered
method we can add a listener using the
web-sys
and
wasm-bindgen API.
We do this in rendered
as this is the only time we can guarantee that the element exists in
the browser, Yew needs some time to create them after view
is called.
The examples below are going to show adding listeners for the made-up custard
event. All events
either unsupported by yew or custom can be represented as a
web_sys::Event
. If you
need to access a specific method or field on a custom / unsupported event then you can use the
methods of JsCast
in order to convert to the type required.