Docs for all DOM operation wrappers

This commit is contained in:
Greg Johnston 2022-11-10 22:09:28 -05:00
parent e19dd0a226
commit 9a59c371fd
1 changed files with 64 additions and 34 deletions

View File

@ -10,40 +10,61 @@ thread_local! {
pub static DOCUMENT: web_sys::Document = web_sys::window().unwrap_throw().document().unwrap_throw();
}
/// Returns the [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window).
///
/// This is cached as a thread-local variable, so calling `window()` multiple times
/// requires only one call out to JavaScript.
pub fn window() -> web_sys::Window {
WINDOW.with(|window| window.clone())
}
/// Returns the [`Document`](https://developer.mozilla.org/en-US/docs/Web/API/Document).
///
/// This is cached as a thread-local variable, so calling `window()` multiple times
/// requires only one call out to JavaScript.
pub fn document() -> web_sys::Document {
DOCUMENT.with(|document| document.clone())
}
/// Returns the `<body>` elements of the current HTML document, if it exists.
pub fn body() -> Option<web_sys::HtmlElement> {
document().body()
}
/// Creates a DOM [`Element`](https://developer.mozilla.org/en-US/docs/Web/API/Element). See
/// [`Document.createElement`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement).
pub fn create_element(tag_name: &str) -> web_sys::Element {
document().create_element(tag_name).unwrap_throw()
}
/// Creates a DOM [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text) node. See
/// [`Document.createTextNode`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createTextNode).
pub fn create_text_node(data: &str) -> web_sys::Text {
document().create_text_node(data)
}
/// Creates a [`DocumentFragment`](https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment). See
/// [`Document.createElement`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createDocumentFragment).
pub fn create_fragment() -> web_sys::DocumentFragment {
document().create_document_fragment()
}
/// Creates a [`Comment`](https://developer.mozilla.org/en-US/docs/Web/API/Comment) node.
/// See [`Document.createCommentNode`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createComment).
pub fn create_comment_node() -> web_sys::Node {
document().create_comment("").unchecked_into()
}
/// Creates an [`HTMLTemplateElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTemplateElement)
/// and sets its `innerHTML` to the given HTML string.
pub fn create_template(html: &str) -> web_sys::HtmlTemplateElement {
let template = create_element("template");
template.set_inner_html(html);
template.unchecked_into()
}
/// Clones an an [`HTMLTemplateElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTemplateElement)
/// and returns its first element child.
pub fn clone_template(template: &web_sys::HtmlTemplateElement) -> web_sys::Element {
template
.content()
@ -54,18 +75,26 @@ pub fn clone_template(template: &web_sys::HtmlTemplateElement) -> web_sys::Eleme
.unchecked_into()
}
/// Appends a child node to the parent element.
/// See [`Node.appendChild`](https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild).
pub fn append_child(parent: &web_sys::Element, child: &web_sys::Node) -> web_sys::Node {
parent.append_child(child).unwrap_throw()
}
/// Removes the child node from its parent element.
/// See [`Node.removeChild`](https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild).
pub fn remove_child(parent: &web_sys::Element, child: &web_sys::Node) {
_ = parent.remove_child(child);
}
/// Replaces the old node with the new one, within the parent element.
/// See [`Node.replaceChild`](https://developer.mozilla.org/en-US/docs/Web/API/Node/replaceChild).
pub fn replace_child(parent: &web_sys::Element, new: &web_sys::Node, old: &web_sys::Node) {
_ = parent.replace_child(new, old);
}
/// Inserts the new node before the existing node (or, if `None`, at the end of the parent's children.)
/// See [`Node.insertBefore`](https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore).
pub fn insert_before(
parent: &web_sys::Element,
new: &web_sys::Node,
@ -92,22 +121,30 @@ pub fn insert_before(
}
}
/// Replace the old node with the new node in the DOM.
/// See [`Element.replaceWith`](https://developer.mozilla.org/en-US/docs/Web/API/Element/replaceWith).
pub fn replace_with(old_node: &web_sys::Element, new_node: &web_sys::Node) {
_ = old_node.replace_with_with_node_1(new_node);
}
/// Sets the text of a DOM text node.
pub fn set_data(node: &web_sys::Text, value: &str) {
node.set_data(value);
}
/// Sets the value of an attribute on a DOM element.
/// See [`Element.setAttribute`](https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute).
pub fn set_attribute(el: &web_sys::Element, attr_name: &str, value: &str) {
_ = el.set_attribute(attr_name, value);
}
/// Removes an attribute from a DOM element.
/// See [`Element.removeAttribute`](https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute).
pub fn remove_attribute(el: &web_sys::Element, attr_name: &str) {
_ = el.remove_attribute(attr_name);
}
/// Sets a property on a DOM element.
pub fn set_property(el: &web_sys::Element, prop_name: &str, value: &Option<JsValue>) {
let key = JsValue::from_str(prop_name);
match value {
@ -116,33 +153,13 @@ pub fn set_property(el: &web_sys::Element, prop_name: &str, value: &Option<JsVal
};
}
/// Returns the current [`window.location`](https://developer.mozilla.org/en-US/docs/Web/API/Window/location).
pub fn location() -> web_sys::Location {
window().location()
}
pub fn descendants(el: &web_sys::Element) -> impl Iterator<Item = web_sys::Node> {
let children = el.child_nodes();
(0..children.length()).flat_map({
move |idx| {
let child = children.get(idx);
if let Some(child) = child {
// if an Element, send children
if child.node_type() == 1 {
Box::new(descendants(&child.unchecked_into()))
as Box<dyn Iterator<Item = web_sys::Node>>
}
// otherwise, just the node
else {
Box::new(std::iter::once(child)) as Box<dyn Iterator<Item = web_sys::Node>>
}
} else {
Box::new(std::iter::empty()) as Box<dyn Iterator<Item = web_sys::Node>>
}
}
})
}
/// Current window.location.hash without the beginning #
/// Current [`window.location.hash`](https://developer.mozilla.org/en-US/docs/Web/API/Window/location)
/// without the beginning #.
pub fn location_hash() -> Option<String> {
if is_server!() {
None
@ -151,10 +168,13 @@ pub fn location_hash() -> Option<String> {
}
}
/// Current [`window.location.pathname`](https://developer.mozilla.org/en-US/docs/Web/API/Window/location).
pub fn location_pathname() -> Option<String> {
location().pathname().ok()
}
/// Helper function to extract [`Event.target`](https://developer.mozilla.org/en-US/docs/Web/API/Event/target)
/// from any event.
pub fn event_target<T>(event: &web_sys::Event) -> T
where
T: JsCast,
@ -162,6 +182,9 @@ where
event.target().unwrap_throw().unchecked_into::<T>()
}
/// Helper function to extract `event.target.value` from an event.
///
/// This is useful in the `on:input` or `on:change` listeners for an `<input>` element.
pub fn event_target_value(event: &web_sys::Event) -> String {
event
.target()
@ -170,6 +193,9 @@ pub fn event_target_value(event: &web_sys::Event) -> String {
.value()
}
/// Helper function to extract `event.target.checked` from an event.
///
/// This is useful in the `on:change` listeners for an `<input type="checkbox">` element.
pub fn event_target_checked(ev: &web_sys::Event) -> bool {
ev.target()
.unwrap()
@ -177,27 +203,22 @@ pub fn event_target_checked(ev: &web_sys::Event) -> bool {
.checked()
}
pub fn event_target_selector(ev: &web_sys::Event, selector: &str) -> bool {
matches!(
ev.target().and_then(|target| {
target
.dyn_ref::<web_sys::Element>()
.map(|el| el.closest(selector))
}),
Some(Ok(Some(_)))
)
}
/// Runs the given function between the next repaint
/// using [`Window.requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame).
pub fn request_animation_frame(cb: impl Fn() + 'static) {
let cb = Closure::wrap(Box::new(cb) as Box<dyn Fn()>).into_js_value();
_ = window().request_animation_frame(cb.as_ref().unchecked_ref());
}
/// Queues the given function during an idle period
/// using [`Window.requestIdleCallback`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestIdleCallback).
pub fn request_idle_callback(cb: impl Fn() + 'static) {
let cb = Closure::wrap(Box::new(cb) as Box<dyn Fn()>).into_js_value();
_ = window().request_idle_callback(cb.as_ref().unchecked_ref());
}
/// Executes the given function after the given duration of time has passed.
/// [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout).
pub fn set_timeout(cb: impl FnOnce() + 'static, duration: Duration) {
let cb = Closure::once_into_js(Box::new(cb) as Box<dyn FnOnce()>);
_ = window().set_timeout_with_callback_and_timeout_and_arguments_0(
@ -206,15 +227,20 @@ pub fn set_timeout(cb: impl FnOnce() + 'static, duration: Duration) {
);
}
/// Handle that is generated by [set_interval] and can be used to clear the interval.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct IntervalHandle(i32);
impl IntervalHandle {
/// Cancels the repeating event to which this refers.
/// See [`clearInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval)
pub fn clear(&self) {
window().clear_interval_with_handle(self.0);
}
}
/// Repeatedly calls the given function, with a delay of the given duration between calls.
/// See [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval).
pub fn set_interval(
cb: impl Fn() + 'static,
duration: Duration,
@ -227,6 +253,7 @@ pub fn set_interval(
Ok(IntervalHandle(handle))
}
/// Adds an event listener to the target DOM element using implicit event delegation.
pub fn add_event_listener(
target: &web_sys::Element,
event_name: &'static str,
@ -238,11 +265,13 @@ pub fn add_event_listener(
event_delegation::add_event_listener(event_name);
}
#[doc(hidden)]
#[inline(always)]
pub fn ssr_event_listener(_cb: impl FnMut(web_sys::Event) + 'static) {
// this function exists only for type inference in templates for SSR
}
/// Adds an event listener to the `Window`.
pub fn window_event_listener(event_name: &str, cb: impl Fn(web_sys::Event) + 'static) {
if !is_server!() {
let handler = Box::new(cb) as Box<dyn FnMut(web_sys::Event)>;
@ -252,6 +281,7 @@ pub fn window_event_listener(event_name: &str, cb: impl Fn(web_sys::Event) + 'st
}
}
/// Removes all event listeners from an element.
pub fn remove_event_listeners(el: &web_sys::Element) {
let clone = el.clone_node().unwrap_throw();
replace_with(el, clone.unchecked_ref());