Skip to content

Timing of <dialog> 'close' event, popover 'toggle' event, <details> 'toggle' event #9046

Open
@jakearchibald

Description

@jakearchibald

https://html.spec.whatwg.org/multipage/popover.html
https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element
https://html.spec.whatwg.org/multipage/interactive-elements.html#details-notification-task-steps

The show/hide steps for these happen on the same thread. However, a task is queued to fire the "close" and "toggle" events on dialog and popovers respectfully.

This creates a weird situation where the event could fire before or after the next render. Given that "I'm here now!" events are often used by developers as a time to enhance elements, this creates a race condition where the original state may be seen for a frame - a flash of unenhanced content. This would be really difficult to debug.

The solution in the <dialog>/<details> case would be to instead use a mutation observer for the open attribute, since that's guaranteed to happen before rendering. With popover, I guess you could use the "beforetoggle" event, but this event is also cancellable, so it isn't exactly "I'm here now!".

Option 1: Do nothing

Accept that it's a rotten bit of the platform, that will grow, since new features (like popover) will copy the behaviour in the name of consistency.

'select' events on various inputs also queue in this way.

Option 2: Dispatch the event synchronously

This is usually how events work when the action is performed on the main thread.

However, I think there's some reluctance to do this for <dialog> and <details>, since it's related to an attribute change, and for some reason we don't want to fire events in relation to DOM changes?

Also, changing from async to sync might be a compat risk for <dialog> and <details>.

Option 3: Dispatch the event in a microtask

This is how mutation observers work, which are related to DOM changes. It also allows for some debouncing, and it's still async.

Option 4: Dispatch the event in the render steps

This is how resize/scroll/pointer events work.

However, if the dialog is shown after this point in the render steps, you'd still get a flash of unenhanced content.


My preference is for option 2 or 3.

I can't decide if "input" events behave this way. The HTML spec suggests it might, but the UI events spec says it must be dispatched immediately.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions