Skip to content

Commit

Permalink
Check event bubbling cancellation at each step of propagation (#2191)
Browse files Browse the repository at this point in the history
Rather than once after the innermost node is given the chance to
handle it.

Fixes #2185

Co-authored-by: Robert Macomber <robertm@mox>
  • Loading branch information
rjmac and Robert Macomber committed Nov 23, 2021
1 parent c348a23 commit 47fad81
Showing 1 changed file with 40 additions and 5 deletions.
45 changes: 40 additions & 5 deletions packages/yew/src/virtual_dom/listeners.rs
Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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<C>(ctx: &Context<C>, state: &State) -> Html
where
C: Component<Message = Message>,
{
html! {
<div onclick={ctx.link().callback(|_| Message::Action)}>
<div onclick={ctx.link().callback(|event: MouseEvent| {
event.stop_propagation();
Message::Action
})}>
<a>
{state.action}
</a>
</div>
</div>
}
}
}

let (_, el) = init::<CancelBubbling>("a");

assert_count(&el, 0);

el.click();
assert_count(&el, 1);

el.click();
assert_count(&el, 2);
}

fn test_input_listener<E>(make_event: impl Fn() -> E)
where
E: JsCast + std::fmt::Debug,
Expand Down

0 comments on commit 47fad81

Please sign in to comment.