How to add_event_listener_*() with dioxus? #2562
stevepryde
started this conversation in
General
Replies: 2 comments 1 reply
-
I managed to solve this. Sharing the code so that others may benefit too 😄 use dioxus::prelude::*;
use std::{cell::RefCell, ops::Deref, rc::Rc};
use wasm_bindgen::{
closure::Closure,
convert::{FromWasmAbi, RefFromWasmAbi},
JsCast,
};
use web_sys::{Element, EventTarget, HtmlElement, MouseEvent};
#[derive(Clone)]
pub struct EventListenerHandle {
cleanup: Rc<RefCell<Option<Box<dyn FnOnce()>>>>,
}
impl EventListenerHandle {
pub fn new<EventKind, T>(
target_element: T,
event_name: &'static str,
mut callback: impl FnMut(EventKind) + 'static,
) -> Self
where
EventKind: Sized + RefFromWasmAbi + FromWasmAbi + Clone + 'static,
T: Clone + Deref<Target = EventTarget> + std::fmt::Debug + 'static,
{
let closure = Closure::wrap(Box::new(move |event: EventKind| {
callback(event);
}) as Box<dyn FnMut(_)>);
if let Err(e) = target_element
.add_event_listener_with_callback(&event_name, closure.as_ref().unchecked_ref())
{
tracing::error!("failed to add event listener: {e:?}");
}
let cleanup = Rc::new(RefCell::new(Some(Box::new(move || {
if let Err(e) = target_element
.remove_event_listener_with_callback(&event_name, closure.as_ref().unchecked_ref())
{
tracing::error!("failed to remove event listener: {e:?}");
}
}) as Box<dyn FnOnce()>)));
Self { cleanup }
}
pub fn cleanup(&self) {
let cleanup = self.cleanup.borrow_mut().take();
if let Some(cleanup) = cleanup {
cleanup();
}
}
}
impl Drop for EventListenerHandle {
fn drop(&mut self) {
// Only cleanup if this is the last reference.
if Rc::strong_count(&self.cleanup) == 1 {
self.cleanup();
}
}
}
pub fn use_on_event<EventKind, T>(
target_element: &T,
event_name: &'static str,
mut callback: impl FnMut(EventKind) + 'static,
) where
EventKind: Sized + RefFromWasmAbi + FromWasmAbi + Clone + 'static,
T: Clone + Deref<Target = EventTarget> + std::fmt::Debug + 'static,
{
let hook = || {
EventListenerHandle::new(target_element.clone(), event_name, move |kind| {
callback(kind)
})
};
let cleanup = |f: EventListenerHandle| {
f.cleanup();
};
use_hook_with_cleanup(hook, cleanup);
}
pub fn use_outside_click<S: ToString>(id: S, mut callback: impl FnMut(Element) + 'static) {
let window = gloo::utils::window();
let document = gloo::utils::document();
let id = id.to_string();
use_on_event(&window, "mousedown", move |ev: MouseEvent| {
if let Some(target) = ev.target() {
if let Some(dropdown) = document.get_element_by_id(&id) {
let target_element: &HtmlElement = target.unchecked_ref();
let target_node: &web_sys::Node = target_element.as_ref();
if !dropdown.contains(Some(target_node)) {
callback(dropdown);
}
}
}
})
} |
Beta Was this translation helpful? Give feedback.
0 replies
-
I tried to do something similar and used the use dioxus::prelude::*;
use web_sys::{
wasm_bindgen::{prelude::Closure, JsCast},
window, KeyboardEvent,
};
#[component]
pub fn Main() -> Element {
use_hook_with_cleanup(
move || {
let closure = Closure::wrap(Box::new(move |event: KeyboardEvent| todo!("add logic")) as Box<dyn FnMut(_)>);
let document = window()
.unwrap()
.document()
.unwrap();
document.add_event_listener_with_callback("keydown", closure.as_ref().unchecked_ref())
.unwrap();
(document, closure.into_js_value())
},
|(document, closure)| {
document
.remove_event_listener_with_callback("keydown", closure.as_ref().unchecked_ref())
.unwrap();
drop(closure);
},
);
rsx! {}
} |
Beta Was this translation helpful? Give feedback.
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
I'm trying to add an event listener on the window to catch clicks outside a particular element, but I can't find any examples of how to use
add_event_listener_with_callback()
(fromweb_sys
) with dioxus.Also I cannot find a way to get a node ref either. How do you interact with elements directly via web_sys in dioxus?
I can add an event listener but the listener is not
Clone
. So I need to add it inuse_effect()
and destroy it inuse_drop()
but I can't find a way to get the onclick (which is aClosure
type from wasm_bindgen) to be shared across both.If someone has a code example for how to work with event listeners in web_sys within dioxus that would be great. Thanks.
Beta Was this translation helpful? Give feedback.
All reactions