Avoid manual delegation for all the DOM events that don't bubble by default. (This is technically too conservative, as one or two of these only don't bubble on certain elements, but it's simpler than passing in the element name and only a very small deopt in those cases.)

This commit is contained in:
Greg Johnston 2022-11-06 20:00:35 -05:00
parent b9ca0b11a2
commit 36be004ef2
2 changed files with 38 additions and 9 deletions

View File

@ -238,6 +238,15 @@ pub fn add_event_listener(
event_delegation::add_event_listener(event_name);
}
pub fn add_event_listener_undelegated(
target: &web_sys::Element,
event_name: &'static str,
cb: impl FnMut(web_sys::Event) + 'static,
) {
let cb = Closure::wrap(Box::new(cb) as Box<dyn FnMut(web_sys::Event)>).into_js_value();
_ = target.add_event_listener_with_callback(event_name, cb.unchecked_ref());
}
#[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

View File

@ -6,6 +6,8 @@ use uuid::Uuid;
use crate::{is_component_node, Mode};
const NON_BUBBLING_EVENTS: [&str; 11] = ["load", "unload", "scroll", "focus", "blur", "loadstart", "progress", "error", "abort", "load", "loadend"];
pub(crate) fn render_view(cx: &Ident, nodes: &[Node], mode: Mode) -> TokenStream {
let template_uid = Ident::new(
&format!("TEMPLATE_{}", Uuid::new_v4().simple()),
@ -481,9 +483,15 @@ fn attr_to_tokens(
} else {
name.replacen("on-", "", 1)
};
expressions.push(quote_spanned! {
span => add_event_listener(#el_id.unchecked_ref(), #name, #handler);
});
if NON_BUBBLING_EVENTS.contains(&name.as_str()) {
expressions.push(quote_spanned! {
span => ::leptos::add_event_listener_undelegated(#el_id.unchecked_ref(), #name, #handler);
});
} else {
expressions.push(quote_spanned! {
span => ::leptos::add_event_listener(#el_id.unchecked_ref(), #name, #handler);
});
}
} else {
// this is here to avoid warnings about unused signals
// that are used in event listeners. I'm open to better solutions.
@ -932,9 +940,15 @@ fn create_component(cx: &Ident, node: &Node, mode: Mode) -> TokenStream {
.value
.as_ref()
.expect("on: event listener attributes need a value");
Some(quote_spanned! {
span => add_event_listener(#component_name.unchecked_ref(), #event_name, #handler)
})
if NON_BUBBLING_EVENTS.contains(&event_name) {
Some(quote_spanned! {
span => ::leptos::add_event_listener_undelegated(#component_name.unchecked_ref(), #event_name, #handler);
})
} else {
Some(quote_spanned! {
span => ::leptos::add_event_listener(#component_name.unchecked_ref(), #event_name, #handler)
})
}
}
else if let Some(event_name) = attr_name.strip_prefix("on-") {
let span = attr.name_span().unwrap();
@ -942,9 +956,15 @@ fn create_component(cx: &Ident, node: &Node, mode: Mode) -> TokenStream {
.value
.as_ref()
.expect("on- event listener attributes need a value");
Some(quote_spanned! {
span => add_event_listener(#component_name.unchecked_ref(), #event_name, #handler)
})
if NON_BUBBLING_EVENTS.contains(&event_name) {
Some(quote_spanned! {
span => ::leptos::add_event_listener_undelegated(#component_name.unchecked_ref(), #event_name, #handler);
})
} else {
Some(quote_spanned! {
span => ::leptos::add_event_listener(#component_name.unchecked_ref(), #event_name, #handler)
})
}
}
// Properties
else if let Some(name) = attr_name.strip_prefix("prop:") {