From 441cc21830f067aeec3793d2046ba84d95318d13 Mon Sep 17 00:00:00 2001 From: Robert Macomber Date: Tue, 23 Nov 2021 06:37:30 -0800 Subject: [PATCH] Check event bubbling cancellation at each step of propagation Rather than once after the innermost node is given the chance to handle it. Fixes #2185 --- packages/yew/src/virtual_dom/listeners.rs | 45 ++++++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/packages/yew/src/virtual_dom/listeners.rs b/packages/yew/src/virtual_dom/listeners.rs index 0b4e0859012..f6c48e5e30d 100644 --- a/packages/yew/src/virtual_dom/listeners.rs +++ b/packages/yew/src/virtual_dom/listeners.rs @@ -502,16 +502,13 @@ impl Registry { run_handler(&target); - if unsafe { BUBBLE_EVENTS } && !event.cancel_bubble() { + if unsafe { BUBBLE_EVENTS } { let mut el = target; - loop { + while !event.cancel_bubble() { el = match el.parent_element() { Some(el) => el, None => break, }; - // XXX: we have no way to detect, if the callback called `Event.stopPropagation()` - // or `Event.stopImmediatePropagation()` without breaking the callback API. - // It's arguably not worth the cost. run_handler(&el); } } @@ -828,6 +825,44 @@ mod tests { assert_count(&el, 2); } + #[test] + fn cancel_bubbling_nested() { + // Here an event is being delivered to a DOM node which does + // _not_ have a listener but which is contained within an + // element that does and which cancels the bubble. + struct CancelBubbling; + + impl Mixin for CancelBubbling { + fn view(ctx: &Context, state: &State) -> Html + where + C: Component, + { + html! { +
+ +
+ } + } + } + + let (_, el) = init::("a"); + + assert_count(&el, 0); + + el.click(); + assert_count(&el, 1); + + el.click(); + assert_count(&el, 2); + } + fn test_input_listener(make_event: impl Fn() -> E) where E: JsCast + std::fmt::Debug,