html
html copied to clipboard
Mouse events & disabled form controls
Test: http://jsbin.com/botohet/edit?js,output
https://html.spec.whatwg.org/multipage/forms.html#enabling-and-disabling-form-controls:-the-disabled-attribute says:
A form control that is disabled must prevent any click events that are queued on the user interaction task source from being dispatched on the element.
I guess this is weird legacy behaviour, as it completely prevents the event, rather than stopping propagation during the bubbling phase.
Currently Chrome, Edge & Safari apply the same behaviour to all mouse events. I'd much prefer that all browsers switched Firefox's behaviour, but if it's too late to do this, the weird behaviour should probably become part of the spec.
~Unfortunately Chrome & Edge do the same for pointer events, but hopefully that can be changed https://github.com/w3c/pointerevents/issues/177.~ This was fixed for pointer events.
Someone needs to define "hit testing" basically. That's the root of all these issues.
??? Hit-testing is clearly working correctly here - it correctly hit-tests that you're over a disabled form control, and then swallows the mouse event. That's the issue here.
Yeah, I got this confused with defining mouse events, which need hit testing to be properly defined (hit testing would tell them it's a disabled control, at which point they wouldn't dispatch the mouse event, but maybe would dispatch the pointer event in the same task).
@RByers @jacobrossi any insight into where this weird behaviour comes from?
Wow, this is news to me! Since the main thing you're talking about isn't actually defined by HTML, let's discuss over in https://github.com/w3c/pointerevents/issues/177. Once we figure that out, let's come back to the click behavior defined by HTML - maybe we can eliminate that oddity too?
Note that the issue description here is not quite right about what the "firefox behavior" is or the other browsers' behavior, afaict. See https://lists.w3.org/Archives/Public/public-html/2015Oct/0010.html
It looks like Gecko basically stops preventing the dispatching of all events on disabled form controls while WebKit and Blink seem to simply ignore event listeners on disabled form controls.
See https://gist.github.com/rniwa/bf0f1411d6b811fcb605e796498740f3
Gecko yields nothing while WebKit and Blink yields:
click on div
test on button
test on div
I updated my test case a bit (updated the gist). It looks like Gecko is simply truncating the event path at the first disabled form control. This behavior is saner than WebKit/Blink's.
I do have a mild concern that preventing all events from being dispatched on a disabled form control might be a breaking change for some websites. Given WebKit/Blink only has this quirk for MouseEvent, that might be good enough.
I'd also add that while WebKit/Blink behavior is very odd, it probably has the least effect to the rest of the event dispatching behavior so it might be something to consider.
Blink has since changed behaviour for mouse events. http://output.jsbin.com/botohet/quiet - the listener here is on the window object (capturing phase). You get mouse/down/up events on the disabled button in Blink, whereas you don't in WebKit.
You mean pointer events? I don't see any mousemove or mousedown on Chrome 71.0.3544.2
I'm getting both mouse & pointer events in 68 and 71.
@rniwa apologies. It only fires mouse events with the "Experimental web platform" flag.
It's 2019. With https://jsbin.com/cafidayeri/1/edit?html,js,console,output, Firefox seems to allow any synthetic events to be fired on the element itself, except click events coming from the browser – precisely the spec behavior. On the other hand, Chrome prevents any MouseEvent from firing on the element itself. This includes not just click events, but also mousemove events, either synthetic or coming from the browser.
Both browsers prevent click() from doing anything if a button is disabled.
Relevant WPT: https://github.com/web-platform-tests/wpt/blob/master/html/semantics/forms/attributes-common-to-form-controls/disabled-elements-01.html. This checks that synthetic non-MouseEvent event goes through, which seems to be just about the only thing the two browsers agree on.
| kind | click MouseEvent |
mousemove MouseEvent |
click Event |
mousemove Event |
click() |
|---|---|---|---|---|---|
| synthetic | Chrome no, FF yes, spec yes | Chrome no, FF yes, spec yes | Chrome yes, FF yes, spec ambiguous-to-yes*; WPT-tested | all yes | all no |
| natural | all no | Chrome no, FF yes, spec yes |
*: spec says “click events”; it’s unclear whether this involves checking if the event is an MouseEvent object or not, though I’m inclined to say yes it does as that’s what Chrome and FF agree on.
For the record, @eps1lon ran into this, and @brendo tried to fix it, in jsdom: https://github.com/jsdom/jsdom/issues/2665 and https://github.com/jsdom/jsdom/pull/2681. This is quite the interop footgun...
The test case there was very simple: just someDisabledButtonElement.dispatchEvent(new MouseEvent('click')). That will trigger any click listeners in Firefox, and not in Chrome. (I can't test WebKit right now.) I guess reading the above thread though, the mess is much bigger than that.
Oh wow, @TimothyGu posted a much larger analysis while I was writing this, I see :).
/cc @tkent-google since I know he likes interop bugs and form controls.
That will trigger any click listeners in Firefox, and not in Chrome. (I can't test WebKit right now.)
Based on what I tested on #5000, WebKit does not trigger click listeners. (But interestingly mutates checkboxes!)
I want to share a case related to focus events.
If you have a non-focusable element e.g. <div /> you can div.dispatchEvent(new FocusEvent('focus')) and any focus event listener on the div will be executed. On the other hand calling div.focus() will not dispatch any event.
Dispatching click events on a disabled button will not execute click handlers on that button in Chrome. It will also not dispatch click events when calling button.click()
A div without a tabIndex acts with regard to focus like a disabled button acts with regard to click.
IMO I'd prefer to not prevent synthetic events. While this is a somewhat common footgun when writing tests I would always recommend using an imperative method if it exists rather than dispatching synthetic events.
Firefox now seems to be shipping 2 different site patches to work around sites which depend on the Blink/WebKit behaviour here (c.f. bug 1653882). To me it seems hard to justify any course of action other than aligning the spec with the majority of implementations and updating Gecko to match.
I and Anne made a table for different browser behaviors: https://docs.google.com/document/d/18OEhfqA7yYJWcvHKlzldkrg7SzseFGqKZNFlL0CBiec/preview
tl;dr: Firefox never bubbles click events from disabled form elements, while Chrome and Safari are kinda arbitrary.
One thing of note is that Chrome behaves rather differently if you have "Experimental Web Platform features" enabled (click doesn't dispatch on any disabled control, but mousedown does on all of them).
Here is the status of chrome's behavior:
@dtapuska tried to fire mouse events on disabled form controls years ago, and this behavior is still enabled in experimental web platform features under the SendMouseEventsDisabledFormControls flag. Here are the blink-dev threads for this change:
- https://groups.google.com/a/chromium.org/g/blink-dev/c/rhNbsYDBJes/m/Jvmtv6IbBwAJ
- https://groups.google.com/a/chromium.org/g/blink-dev/c/KHXNJINUrj0/m/OAAh-sWuAwAJ
In the second thread, there was some concern raised for changing this behavior, so Dave made a UseCounter which can be seen here on chromestatus: https://chromestatus.com/metrics/feature/timeline/popularity/2321
After reading the second blink-dev thread and doing some more experimenting, it looks like the concern was about whether we should be firing mousedown/mouseup in addition to mousemove, since neither firefox nor safari fire mousedown/mouseup on Jake's webpage. I'm not sure what the motivation was for or against mousedown/mouseup, and I'm not sure how to interpret the chromestatus UseCounter numbers...
Making one effort to get the right behavior with disabled form controls seems like a big task because there are many different chrome bugs about disabled form controls, many spec issues, many different types of events, different types of elements, different behavior for bubbling, several WPTs, etc:
- https://bugs.chromium.org/p/chromium/issues/detail?id=588760
- https://bugs.chromium.org/p/chromium/issues/detail?id=693521
- https://bugs.chromium.org/p/chromium/issues/detail?id=1115661
- https://bugs.chromium.org/p/chromium/issues/detail?id=1116161
- https://bugs.chromium.org/p/chromium/issues/detail?id=1169799
- https://bugs.chromium.org/p/chromium/issues/detail?id=1245511
- https://bugs.chromium.org/p/chromium/issues/detail?id=1061240
- https://github.com/whatwg/html/issues/4328
- https://github.com/whatwg/html/issues/5000
- https://github.com/whatwg/html/issues/5886
- https://github.com/whatwg/html/pull/5805
- https://github.com/whatwg/dom/issues/687
- https://github.com/w3c/pointerevents/issues/177
- https://wpt.fyi/results/dom/events/Event-dispatch-click.html (test cases with "disabl" in their name)
If just firing "mousemove" on disabled form controls like chrome currently does with experimental web platform features enabled is a step in the right direction, then I guess we can just ship that and put off the whole mousedown/mouseup thing since no other browsers are currently doing it...? I could also spend more time trying to get a better understanding of all the different scenarios.
I've been looking at this more recently and I have some new thoughts.
Jake's article and this issue advocate for as many events as possible, including mouseup and mousedown. mousedown and mouseup do seem more problematic since they are similar to the click event and could make a website think that the button is not disabled. I don't know for certain what would happen if we do ship mouseup and mousedown, but I think there is a high risk to websites especially since the UseCounter that @dtapuska added shows 0.11% of page loads listening to mouseup and mousedown on disabled form controls, and because no stable browser currently does mouseup or mousedown.
I think that we should allow all events on disabled form controls except mouseup, mousedown, and click. After getting this done, then we can focus on some more of the edge cases I listed in my previous comment if any are still relevant at that point.
Does anyone have any final thoughts on whether or not to fire mouseup and mousedown on disabled form controls? I said in my last comment that I will make chromium not fire mouseup and mousedown on disabled form controls, and I am planning on trying to ship it soon. We were ideally going to fire mouseup and mousedown until this comment was made: https://groups.google.com/a/chromium.org/g/blink-dev/c/rhNbsYDBJes/m/ugP5cTQeBwAJ
I filed an intent to ship in chromium: https://groups.google.com/a/chromium.org/g/blink-dev/c/9i0H0J0BzE4
Are we ready for a spec / web platform tests change here yet? I'm excited to close this issue, after 6 years. (Or 8, if you count https://lists.w3.org/Archives/Public/public-html/2015Oct/0010.html.) Maybe we can also close some of the others listed in https://github.com/whatwg/html/issues/2368#issuecomment-1029307050 , not sure.
I just enabled the change on stable chrome today, I'd like to wait a few more days before being certain that it's not breaking anything. I haven't heard anything yet though which is a good sign!
In terms of WPTs, I wrote a basic test in html/semantics/disabled-elements/disabled-event-dispatch.tentative.html and @saschanaz was working on a more exhaustive test if I remember correctly.
I'm happy to write spec PRs assuming nobody else is jumping at the opportunity.
Maybe we can also close some of the others listed in https://github.com/whatwg/html/issues/2368#issuecomment-1029307050 , not sure.
I need to take a closer look at https://github.com/whatwg/html/issues/4328 and https://github.com/whatwg/dom/issues/687, but https://github.com/whatwg/html/issues/5886 we can for sure close when we close this one.
In terms of WPTs, I wrote a basic test in
html/semantics/disabled-elements/disabled-event-dispatch.tentative.htmland @saschanaz was working on a more exhaustive test if I remember correctly.
https://wpt.fyi/results/html/semantics/disabled-elements/event-propagate-disabled.tentative.html?label=experimental&label=master&aligned is live too!
Does anyone have any final thoughts on whether or not to fire mouseup and mousedown on disabled form controls?
I have a <button> where I call .preventDefault() on the mousedown event to prevent bluring (focusout, actually) an active (contenteditable) editor. Now when the button is disabled, the mousedown event does not fire but the focusout event still does. I would have expected that either both or neither do fire.
I have a
<button>where I call .preventDefault() on the mousedown event to prevent bluring (focusout, actually) an active (contenteditable) editor. Now when the button is disabled, the mousedown event does not fire but the focusout event still does. I would have expected that either both or neither do fire.
Have you tried listening to pointerdown or pointerup instead? Have you tried adding pointer-events:none to the button?
An issue was raised about the dblclick event on disabled form controls was opened here: https://bugs.chromium.org/p/chromium/issues/detail?id=1497354
With the new behavior I implemented in chrome, dblclick events are now fired on all disabled form control elements. Previously, they would not be fired on disabled buttons but were probably fired on disabled input elements and others which have a user agent shadowroot.
Should we add dblclick to the list of events we don't fire, which currently includes "click", "mouseup", and "mousedown"?
Edit: I just tested out the behavior here: https://jsfiddle.net/jarhar/b2u7x3ys/ Firefox nightly does not fire dblclick events for disabled inputs or disabled buttons. Safari TP and Chrome both fire dblclick events for disabled inputs and disabled buttons.
Blocking dblclick sounds reasonable to me, yes. Gecko doesn't fire contextmenu event either; the allowed event types are currently allowlisted: https://searchfox.org/mozilla-central/rev/99a9eed20cf195b8ff815604876b6fb73ca5ecd7/dom/html/nsGenericHTMLElement.cpp#2003-2049 (returning false means it's allowed on disabled elements)
Edit: There was an attempt a few years ago to convert the list into a denylist but it didn't happen. https://phabricator.services.mozilla.com/D30345