mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
* tree-wide: update links to https://rustwasm.github.io/wasm-bindgen https://rustwasm.github.io/wasm-bindgen -> https://wasm-bindgen.github.io/wasm-bindgen https://rustwasm.github.io/docs/wasm-pack -> https://drager.github.io/wasm-pack/book/ https://rustwasm.github.io/wasm-pack -> https://github.com/drager/wasm-pack
246 lines
8.2 KiB
Plaintext
246 lines
8.2 KiB
Plaintext
---
|
|
title: 'web-sys'
|
|
description: 'The web-sys crate provides bindings for Web APIs.'
|
|
---
|
|
|
|
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 names are so long and why some types are vague.
|
|
|
|
## Features in `web-sys`
|
|
|
|
The `web-sys` crate with all of its features enabled can add lots of bloat to a Wasm application.
|
|
To get around this issue most types are feature gated so that you only include the types
|
|
you require for your application. Yew enables several features from `web-sys` and
|
|
exposes some types in its 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.mdx#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 its inheritance.
|
|
|
|
This section is going to look at a specific element and list out its 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.mdx#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 have 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 have 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://wasm-bindgen.github.io/wasm-bindgen/web-sys/inheritance.html)._
|
|
|
|
## The `Node` in `NodeRef`
|
|
|
|
Yew uses a [`NodeRef`](concepts/function-components/node-refs.mdx) to provide a way for keeping a reference to
|
|
a `Node` made by the [`html!`](concepts/html/introduction.mdx) macro. The `Node` part of `NodeRef` is referring to
|
|
[`web_sys::Node`](https://wasm-bindgen.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 its specific methods. This casting
|
|
can be done using [`JsCast`](./wasm-bindgen.mdx#JsCast) on the `Node` value, if present, but Yew
|
|
provides the `NodeRef::cast` method to perform this casting for convenience and so that you do not
|
|
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://wasm-bindgen.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 demonstrates examples of how JavaScript code which interact with the
|
|
Web APIs can be rewritten with `web-sys` in Rust.
|
|
|
|
### 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 JavaScript 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, or otherwise will
|
|
cause a panic in Rust. Another part of the verbosity is the calls to `JsCast` to cast into
|
|
different types so that you can call its specific methods.
|
|
|
|
### Yew example
|
|
|
|
In Yew you will mostly be creating [`Callback`](concepts/function-components/callbacks.mdx)s to use in the
|
|
[`html!`](concepts/html/introduction.mdx) 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 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
|
|
provide abstractions over `web-sys` to provide more idiomatic Rust APIs.
|
|
|
|
_[External libraries page](/community/external-libs)_
|