You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Popovers are broken when shown in a contextmenu event listener #10905
If you want to use a popover for a context menu, you cannot use the contextmenu event to display the popover. The problem is that the popover is immediately light dismissed.
(not sure if this belongs in whatwg/html or whatwg/dom)
Perhaps the light dismiss behavior should be tweaked to not close any popovers that are shown immediately before in contextmenu, mousedown, mouseup, pointerdown, pointerup, touchstart, touchend event listeners?
That doesn't work in Chrome, FF or Safari. Once you increase the timeout to at least 100ms, then it sometimes works. But at that point you start to have a noticeable delay.
That doesn't work in Chrome, FF or Safari. Once you increase the timeout to at least 100ms, then it sometimes works. But at that point you start to have a noticeable delay.
Ah, interesting. I just tested it, the reason is because contextmenu is fired on mouse down, but on mouse up the popover will be closed.
To test this, try wrapping the showPopover() in setTimeout(fn, 0), and then start right clicking but hold the click. The popover will show. As soon as you release the click the popover is closed.
What's also interesting is it doesn't seem that the "close popovers on click/mouseup" can be canceled. At least no combination that I tried with canceling click/mouseup/pointerup resulted in canceling the popover close. So that doesn't seem like a viable option either.
popover="manual" would work of course, but then you have to reimplement light dismiss.
Yeah, we are running light dismiss kind of unconditionally whenever there is a pointerup event. Maybe we should add a flag for the case that a popover is shown in between pointerdown and pointerup and skip doing light dismiss in that case? I don't think that would help with #10890 (comment) though.
Yeah, we are running light dismiss kind of unconditionally whenever there is a pointerup event. Maybe we should add a flag for the case that a popover is shown in between pointerdown and pointerup and skip doing light dismiss in that case?
Yeah, this sounds like a good solution to me. Might be tricky to keep track of which popover(s) were open, but we can probably do something. Does this sound like a reasonable solution to others?
Perhaps it could, basically if we keep track of the list of open popovers during pointerdown, and compare to pointerup, and exempt any popovers that changed (either direction) from the light dismiss behavior?
Another solution is to check for the event button of the pointerup event? (Just to be clear, I don't have a strong preference on how to resolve this, just throwing options out there)
Another solution is to check for the event button of the pointerup event? (Just to be clear, I don't have a strong preference on how to resolve this, just throwing options out there)
Sounds interesting, but help me understand. In the repro for this issue, the pointerdown and pointerup both happen on the same <span>, don't they?
If you right click, there should be a different event.button used right? Why should right click trigger light dismiss in the first place?
Ahh, mouse "button", not "the <button>". Thanks. I think normal behavior for transient things is that any mouse click, regardless of the button being pressed, light-dismisses popovers. (Unless the right-click is on the popover, of course.) For example, the UA-provided context menu itself is roughly a popover, and if you right-click somewhere else, the first one closes.
If you mousedown over the span, move the mouse elsewhere and then release when it's not over the span, it still light dismisses.
You mean right-mouse-down, etc.? Yeah, mouse drags also (per spec) light dismiss the popover, unless the drag starts or ends on the popover. That's so that you can e.g. select text on the popover.
Aside from technical questions: a common way to invoke a context menu item is to mousedown, hover over a specific menu item and the release.
That is common, at least on Mac platforms. I do think we should work on a solution to this issue. I'd love to make that solution also fix #10890 (comment).
I needed the contextmenu event to immediately open the popover, so that menu items could be selected on mouseup like native context menus. So I open the popover in manual mode and then wait for a pointerup, pointerleave, or pointercancel to switch to auto mode.
The downside to this approach is that changing el.popover="auto" also closes the popover (and that event can't be cancelled either). So you have to re-open the popover immediately after and ignore any spurious beforetoggle and toggle events.
What if we set the popover pointerdown target to this element at the end of the show popover steps? I tested it out locally and it fixes the repro in the OP
What if we set the popover pointerdown target to this element at the end of the show popover steps? I tested it out locally and it fixes the repro in the OP
That sounds like a plan? Guess we would need some WPTs to cover this? This would mean a spec change as well?
I assume this covers touchstart & mousedown as well?
I think so, but what touch gesture on which platform would you use to open a context menu? Want to make sure I'm not missing anything.
I would think doing a long press (that starts with a touchstart) on iOS would trigger the contextmenu event, but it seems it doesn't? https://bugs.webkit.org/show_bug.cgi?id=213953 The last comment implies it works like that on Android at least.
A workaround that works today, for anyone hitting this:
el.addEventListener('contextmenu',e=>{e.preventDefault();popover.showPopover();// pointerup is still pending on Linux/macOS: capture it, so it doesn't light-dismiss the popoverif(e.buttons)popover.setPointerCapture(e.pointerId);});
On Linux and macOS, contextmenu fires between pointerdown and pointerup. With pointer capture, the pending pointerup targets the popover. Because the pointerdown and pointerup targets now differ, light dismiss doesn't happen.
e.buttons is 0 when there is no pending pointerup, so nothing is captured in those cases:
On Windows, contextmenu fires after pointerup.
A menu opened from the keyboard has no pointer involved.
Tested with real input events:
Firefox 156 (Linux): broken without the workaround, works with it.
Chrome 154 (Linux): already works without it (probably because of the click-based light dismiss from Change light dismiss to use click events #11536). The workaround doesn't break anything there.
In both browsers, clicking a menu item and clicking outside still behave as expected.
Activity
jpzwarte commentedon Jan 9, 2025
Perhaps the light dismiss behavior should be tweaked to not close any popovers that are shown immediately before in
contextmenu,mousedown,mouseup,pointerdown,pointerup,touchstart,touchendevent listeners?mkrause commentedon Jan 10, 2025
Maybe wrap it in a
setTimeout(fn, 0)so the popover gets shown after the event.jpzwarte commentedon Jan 10, 2025
That doesn't work in Chrome, FF or Safari. Once you increase the timeout to at least 100ms, then it sometimes works. But at that point you start to have a noticeable delay.
mkrause commentedon Jan 10, 2025
Ah, interesting. I just tested it, the reason is because
contextmenuis fired on mouse down, but on mouse up the popover will be closed.To test this, try wrapping the
showPopover()insetTimeout(fn, 0), and then start right clicking but hold the click. The popover will show. As soon as you release the click the popover is closed.What's also interesting is it doesn't seem that the "close popovers on click/mouseup" can be canceled. At least no combination that I tried with canceling click/mouseup/pointerup resulted in canceling the popover close. So that doesn't seem like a viable option either.
popover="manual"would work of course, but then you have to reimplement light dismiss.mkrause commentedon Jan 10, 2025
Found a workaround by using
mouseupinstead and checking for right click (or CTRL+click on Mac).No
setTimeoutnecessary when done inmouseup. Tested it in Chrome, Firefox, and Safari and works in all of them.annevk commentedon Jan 16, 2025
This sounds similar to #10890 (comment).
cc @josepharhar @nt1m @mfreed7
josepharhar commentedon Jan 22, 2025
Yeah, we are running light dismiss kind of unconditionally whenever there is a pointerup event. Maybe we should add a flag for the case that a popover is shown in between pointerdown and pointerup and skip doing light dismiss in that case? I don't think that would help with #10890 (comment) though.
mfreed7 commentedon Jan 23, 2025
Interesting bug!
Yeah, this sounds like a good solution to me. Might be tricky to keep track of which popover(s) were open, but we can probably do something. Does this sound like a reasonable solution to others?
Perhaps it could, basically if we keep track of the list of open popovers during pointerdown, and compare to pointerup, and exempt any popovers that changed (either direction) from the light dismiss behavior?
nt1m commentedon Jan 23, 2025
Another solution is to check for the event button of the pointerup event? (Just to be clear, I don't have a strong preference on how to resolve this, just throwing options out there)
mfreed7 commentedon Jan 23, 2025
Sounds interesting, but help me understand. In the repro for this issue, the pointerdown and pointerup both happen on the same
<span>, don't they?nt1m commentedon Jan 24, 2025
If you right click, there should be a different event.button used right? Why should right click trigger light dismiss in the first place?
jpzwarte commentedon Jan 24, 2025
If you mousedown over the span, move the mouse elsewhere and then release when it's not over the span, it still light dismisses.
Aside from technical questions: a common way to invoke a context menu item is to mousedown, hover over a specific menu item and the release.
mfreed7 commentedon Jan 25, 2025
Ahh, mouse "button", not "the
<button>". Thanks. I think normal behavior for transient things is that any mouse click, regardless of the button being pressed, light-dismisses popovers. (Unless the right-click is on the popover, of course.) For example, the UA-provided context menu itself is roughly a popover, and if you right-click somewhere else, the first one closes.You mean right-mouse-down, etc.? Yeah, mouse drags also (per spec) light dismiss the popover, unless the drag starts or ends on the popover. That's so that you can e.g. select text on the popover.
That is common, at least on Mac platforms. I do think we should work on a solution to this issue. I'd love to make that solution also fix #10890 (comment).
webstrand commentedon Mar 11, 2025
I needed the
contextmenuevent to immediately open the popover, so that menu items could be selected onmouseuplike native context menus. So I open the popover inmanualmode and then wait for apointerup,pointerleave, orpointercancelto switch toautomode.The downside to this approach is that changing
el.popover="auto"also closes the popover (and that event can't be cancelled either). So you have to re-open the popover immediately after and ignore any spuriousbeforetoggleandtoggleevents.josepharhar commentedon Mar 13, 2025
What if we set the popover pointerdown target to this element at the end of the show popover steps? I tested it out locally and it fixes the repro in the OP
jpzwarte commentedon Mar 14, 2025
That sounds like a plan? Guess we would need some WPTs to cover this? This would mean a spec change as well?
I assume this covers touchstart & mousedown as well?
josepharhar commentedon Mar 15, 2025
Yes, yes, and yes
I think so, but what touch gesture on which platform would you use to open a context menu? Want to make sure I'm not missing anything.
jpzwarte commentedon Mar 15, 2025
I would think doing a long press (that starts with a
touchstart) on iOS would trigger thecontextmenuevent, but it seems it doesn't? https://bugs.webkit.org/show_bug.cgi?id=213953 The last comment implies it works like that on Android at least.But from reading https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerdown_event, i think
pointerdowncovers bothmousedownandtouchstart. Long press not triggering contextmenu is probably out of scope of this issue.josepharhar commentedon Apr 1, 2025
I'm going to try out looking at event.button in popover light dismiss to consider not running light dismiss.
Don't run light dismiss on right click
josepharhar commentedon Apr 3, 2025
I created a PR to fix this by looking at event.button as I mentioned my last comment: #11185
josepharhar commentedon Jun 30, 2025
My prototype fix for w3c/pointerevents#542 seems to fix this, so #11185 might not be necessary
Change light dismiss to use click events
refactor(navigator): event-based file context menu plumbing
nuxodin commentedon Sep 23, 2026
A workaround that works today, for anyone hitting this:
On Linux and macOS,
contextmenufires betweenpointerdownandpointerup. With pointer capture, the pendingpointeruptargets the popover. Because the pointerdown and pointerup targets now differ, light dismiss doesn't happen.e.buttonsis 0 when there is no pendingpointerup, so nothing is captured in those cases:contextmenufires afterpointerup.Tested with real input events:
In both browsers, clicking a menu item and clicking outside still behave as expected.