Skip to content

Bug: <dialog> onClose and onCancel events bubbling #34038

@heguro

Description

@heguro

React version: 19.1.1
Chrome version: 138.0.7204.169

Steps To Reproduce

  1. Click "Open Outer Dialog"
  2. Click "Open Inner Dialog"
  3. Click "Close Inner Dialog" or press Esc on keyboard

Link to code example:

React repro: https://codesandbox.io/p/sandbox/qkndj4
Plain JS comparison: https://codepen.io/heguro/pen/XJmNeJr

The current behavior

In the React repro, clicking "Close Inner Dialog" produces the following logs:

inner dialog: onClose
outer dialog: onClose
div: onClose

In plain JS, the close and cancel events do not bubble, so the same operation only outputs inner dialog: onClose.

The expected behavior

Should only output the following log, same as plain JS behavior:

inner dialog: onClose

Additional context

In packages/react-dom-bindings/src/events/DOMPluginEventSystem.js, both 'close' and 'cancel' are listed in nonDelegatedEvents:

// We should not delegate these events to the container, but rather
// set them on the actual target element itself. This is primarily
// because these events do not consistently bubble in the DOM.
export const nonDelegatedEvents: Set<DOMEventName> = new Set([
'beforetoggle',
'cancel',
'close',
'invalid',
'load',
'scroll',
'scrollend',
'toggle',
// In order to reduce bytes, we insert the above array of media events
// into this Set. Note: the "error" event isn't an exclusive media event,
// and can occur on other elements too. Rather than duplicate that event,
// we just take it from the media events array.
...mediaEventTypes,
]);

However, in packages/react-dom-bindings/src/events/plugins/SimpleEventPlugin.js:

// Some events don't bubble in the browser.
// In the past, React has always bubbled them, but this can be surprising.
// We're going to try aligning closer to the browser behavior by not bubbling
// them in React either. We'll start by not bubbling onScroll, and then expand.
const accumulateTargetOnly =
!inCapturePhase &&
// TODO: ideally, we'd eventually add all events from
// nonDelegatedEvents list in DOMPluginEventSystem.
// Then we can remove this special list.
// This is a breaking change that can wait until React 18.
(domEventName === 'scroll' || domEventName === 'scrollend');

The TODO comment mentions plans to add all nonDelegatedEvents to prevent bubbling, originally planned for React 18, but we're now at React 19 and 'close' and 'cancel' events still bubble incorrectly. (Related PR: #19464 #19761)

Edit:

Metadata

Metadata

Assignees

No one assigned

    Labels

    Resolution: StaleAutomatically closed due to inactivityStatus: UnconfirmedA potential issue that we haven't yet confirmed as a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions