How to add_event_listener_*() with dioxus? #2562
stevepryde
started this conversation in
General
Replies: 1 comment
-
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
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