mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
* change suffixes from md to mdx fix broken links for English locale tree shake and update docusaurus add docusaurus ideal image plugin use svg and themed image delete unused static asset * move localized landing page * change GitLocalize project page * nit pick * remove ignore to have the block checked
242 lines
8.1 KiB
Plaintext
242 lines
8.1 KiB
Plaintext
import Tabs from '@theme/Tabs';
|
|
import TabItem from '@theme/TabItem';
|
|
|
|
The [`web-sys` crate](https://crates.io/crates/web-sys) provides bindings for Web APIs. This is
|
|
procedurally generated from browser WebIDL which is why some of the names are so long and why
|
|
some of the types are vague.
|
|
|
|
## Features in `web-sys`
|
|
|
|
The `web-sys` crate with all of it's features enabled can add lots of bloat to a Wasm application,
|
|
in order to get around this issue most types are feature gated so that you only include the types
|
|
you require for your application. Yew includes a number of features from `web-sys` and
|
|
exposes some types in it's public API, you will often need to add `web-sys` as a dependency yourself.
|
|
|
|
## Inheritance in `web-sys`
|
|
|
|
In the [Simulating inheritance section](../wasm-bindgen#simulating-inheritance) you can read how in
|
|
general Rust provides an approach to simulate inheritance in JavaScript. This is very important in
|
|
`web-sys` as understanding what methods are available on a type means understanding it's inheritance.
|
|
|
|
This section is going to look at a specific element and list out it's inheritance using Rust by
|
|
calling [`Deref::deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html#tymethod.deref) until
|
|
the value is [`JsValue`](../wasm-bindgen#jsvalue):
|
|
|
|
```rust
|
|
use std::ops::Deref;
|
|
use web_sys::{
|
|
Element,
|
|
EventTarget,
|
|
HtmlElement,
|
|
HtmlTextAreaElement,
|
|
Node,
|
|
};
|
|
|
|
fn inheritance_of_text_area(text_area: HtmlTextAreaElement) {
|
|
// HtmlTextAreaElement is <textarea> in html.
|
|
let html_element: &HtmlElement = text_area.deref();
|
|
|
|
let element: &Element = html_element.deref();
|
|
|
|
let node: &Node = element.deref();
|
|
|
|
let event_target: &EventTarget = node.deref();
|
|
|
|
// Notice we've moved from web-sys types now into built-in
|
|
// JavaScript types which are in the js-sys crate.
|
|
let object: &js_sys::Object = event_target.deref();
|
|
|
|
// Notice we've moved from js-sys type to the root JsValue from
|
|
// the wasm-bindgen crate.
|
|
let js_value: &wasm_bindgen::JsValue = object.deref();
|
|
|
|
// Using deref like this means we have to manually traverse
|
|
// the inheritance tree, however, you can call JsValue methods
|
|
// on the HtmlTextAreaElement type.
|
|
// The `is_string` method comes from JsValue.
|
|
assert!(!text_area.is_string());
|
|
|
|
// empty function just to prove we can pass HtmlTextAreaElement as a
|
|
// &EventTarget.
|
|
fn this_function_only_takes_event_targets(targets: &EventTarget) {};
|
|
|
|
// The compiler will walk down the deref chain in order to match the types here.
|
|
this_function_only_takes_event_targets(&text_area);
|
|
|
|
// The AsRef implementations allow you to treat the HtmlTextAreaElement
|
|
// as an &EventTarget.
|
|
|
|
let event_target: &EventTarget = text_area.as_ref();
|
|
|
|
}
|
|
```
|
|
|
|
_[Inheritance in `web-sys` in The `wasm-bindgen` Guide](https://rustwasm.github.io/wasm-bindgen/web-sys/inheritance.html)._
|
|
|
|
## The `Node` in `NodeRef`
|
|
|
|
Yew uses a [`NodeRef`](../components/refs) in order to provide a way for keeping a reference to
|
|
a `Node` made by the [`html!`](../html/introduction.mdx) macro. The `Node` part of `NodeRef` is referring to
|
|
[`web_sys::Node`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Node.html). The
|
|
`NodeRef::get` method will return a `Option<Node>` value, however, most of the time in Yew you want
|
|
to cast this value to a specific element so you can use it's specific methods. This casting
|
|
can be done using [`JsCast`](../wasm-bindgen#JsCast) on the `Node` value, if present, but Yew
|
|
provides the `NodeRef::cast` method to perform this casting for convenience and so that you don't
|
|
necessarily have to include the `wasm-bindgen` dependency for the `JsCast` trait.
|
|
|
|
The two code blocks below do essentially the same thing, the first is using `NodeRef::cast` and
|
|
the second is using [`JsCast::dyn_into`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_into)
|
|
on the `web_sys::Node` returned from `NodeRef::get`.
|
|
|
|
<Tabs>
|
|
<TabItem value="Using NodeRef::cast" label="Using NodeRef::cast">
|
|
|
|
```rust
|
|
use web_sys::HtmlInputElement;
|
|
use yew::NodeRef;
|
|
|
|
fn with_node_ref_cast(node_ref: NodeRef) {
|
|
if let Some(input) = node_ref.cast::<HtmlInputElement>() {
|
|
// do something with HtmlInputElement
|
|
}
|
|
}
|
|
```
|
|
|
|
</TabItem>
|
|
<TabItem value="Using NodeRef::get" label="Using NodeRef::get">
|
|
|
|
```rust
|
|
use wasm_bindgen::JsCast;
|
|
use web_sys::HtmlInputElement;
|
|
use yew::NodeRef;
|
|
|
|
fn with_jscast(node_ref: NodeRef) {
|
|
if let Some(input) = node_ref
|
|
.get()
|
|
.and_then(|node| node.dyn_into::<HtmlInputElement>().ok()) {
|
|
// do something with HtmlInputElement
|
|
}
|
|
}
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
|
|
## JavaScript example to Rust
|
|
|
|
This section is to help show that any examples that use JavaScript to interact with the Web APIs
|
|
can be adapted and written using Rust with `web-sys`.
|
|
|
|
### JavaScript example
|
|
|
|
```js
|
|
document.getElementById('mousemoveme').onmousemove = (e) => {
|
|
// e = Mouse event.
|
|
var rect = e.target.getBoundingClientRect();
|
|
var x = e.clientX - rect.left; //x position within the element.
|
|
var y = e.clientY - rect.top; //y position within the element.
|
|
console.log("Left? : " + x + " ; Top? : " + y + ".");
|
|
}
|
|
```
|
|
|
|
### `web-sys` example
|
|
Using `web-sys` alone the above JavaSciprt example could be implemented like this:
|
|
|
|
```toml title=Cargo.toml
|
|
[dependencies]
|
|
wasm-bindgen = "0.2"
|
|
|
|
[dependencies.web-sys]
|
|
version = "0.3"
|
|
# We need to enable all the web-sys features we want to use!
|
|
features = [
|
|
"console",
|
|
"Document",
|
|
"HtmlElement",
|
|
"MouseEvent",
|
|
"DomRect",
|
|
]
|
|
```
|
|
|
|
```rust ,no_run
|
|
use wasm_bindgen::{prelude::Closure, JsCast};
|
|
use web_sys::{console, Document, HtmlElement, MouseEvent};
|
|
|
|
let mousemove = Closure::<dyn Fn(MouseEvent)>::wrap(Box::new(|e| {
|
|
let rect = e
|
|
.target()
|
|
.expect("mouse event doesn't have a target")
|
|
.dyn_into::<HtmlElement>()
|
|
.expect("event target should be of type HtmlElement")
|
|
.get_bounding_client_rect();
|
|
let x = (e.client_x() as f64) - rect.left();
|
|
let y = (e.client_y() as f64) - rect.top();
|
|
console::log_1(&format!("Left? : {} ; Top? : {}", x, y).into());
|
|
}));
|
|
|
|
Document::new()
|
|
.expect("global document not set")
|
|
.get_element_by_id("mousemoveme")
|
|
.expect("element with id `mousemoveme` not present")
|
|
.unchecked_into::<HtmlElement>()
|
|
.set_onmousemove(mousemove.as_ref().dyn_ref());
|
|
|
|
// we now need to save the `mousemove` Closure so that when
|
|
// this event fires the closure is still in memory.
|
|
```
|
|
|
|
This version is much more verbose, but you will probably notice part of that is because of failure
|
|
types reminding us that some of these function calls have invariants that must be held otherwise will
|
|
cause a panic in Rust. Another part of the verbosity is the calls to `JsCast` in order to cast into
|
|
different types so that you can call it's specific methods.
|
|
|
|
### Yew example
|
|
|
|
In Yew you will mostly be creating [`Callback`](../components/callbacks)s to use in the
|
|
[`html!`](../html/introduction) macro so the example is going to use this approach instead of completely copying
|
|
the approach above:
|
|
|
|
```toml title=Cargo.toml
|
|
[dependencies.web-sys]
|
|
version = "0.3"
|
|
# We need to enable the `DomRect` feature in order to use the
|
|
# `get_bounding_client_rect` method.
|
|
features = [
|
|
"console",
|
|
"HtmlElement",
|
|
"MouseEvent",
|
|
"DomRect",
|
|
]
|
|
|
|
```
|
|
|
|
```rust
|
|
use web_sys::{console, HtmlElement, MouseEvent};
|
|
use yew::{
|
|
html,
|
|
Callback, TargetCast,
|
|
};
|
|
|
|
let onmousemove = Callback::from(|e: MouseEvent| {
|
|
if let Some(target) = e.target_dyn_into::<HtmlElement>() {
|
|
let rect = target.get_bounding_client_rect();
|
|
let x = (e.client_x() as f64) - rect.left();
|
|
let y = (e.client_y() as f64) - rect.top();
|
|
console::log_1(&format!("Left? : {} ; Top? : {}", x, y).into());
|
|
}
|
|
});
|
|
|
|
html! {
|
|
<div id="mousemoveme" {onmousemove}></div>
|
|
};
|
|
```
|
|
|
|
## External libraries
|
|
|
|
`web-sys` is a raw binding to the Web API so it comes with some pain in Rust because it was not
|
|
designed with Rust or even a strong type system in mind, this is where community crates come in to
|
|
provide abstractions over `web-sys` to provide more idiomatic Rust APIs.
|
|
|
|
_[External libraries page](../../more/external-libs)_
|