Open jakearchibald opened 7 years ago
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:
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:
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.html
and @saschanaz was working on a more exhaustive test if I remember correctly.
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 blur
ing (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
Just skimmed UI Events spec and it seems auxclick should also be included in the list.
BTW, having this and #5886 together really doesn't help, can we close either one?
Have you tried listening to pointerdown or pointerup instead? Have you tried adding pointer-events:none to the button?
Thanks for the suggestions!
pointer-events:none
style on the disabled button means the click event does bubble and could be .defaultPrevent()
ed on the parent to prevent the blur, however that is an annoying workaround and also breaks the native cursor: not-allowed
on the disabled buttonpreventDefault()
on the pointerup
event does change nothing, the focusout
still firespreventDefault()
on the pointerdown
event does work great, thanks!
Edit: it doesn't work in Firefox though - it doesn't fire any event at allThis doesn't change my expectation though that focus should not change without a mousedown event, which is traditionally used to prevent blurring.
Blocking dblclick sounds reasonable to me, yes
I just discussed with @smaug---- and they agree with the rationale that "if we block click then we should also block dblclick." I will try going back to this behavior in chrome. To reiterate, this is the current behavior in firefox nightly.
BTW, having this and #5886 together really doesn't help, can we close either one?
This one is for allowing more MouseEvents on disabled form controls, and that one is for the bubbling behavior on disabled form controls. I don't have strong feelings about keeping the other one open so if it helps you then please close it, but the separation is clear in my head.
Hmm, could be good to change the title for this issue, but I don't have permission for that. (The current title sounds like it covers all issues regarding to mouse events and disabled form controls, and also the OP mentions bubbling...)
https://wpt.fyi/results/html/semantics/disabled-elements/disabled-event-dispatch-additional.tentative.html?label=master&label=experimental&aligned The -additional test somehow expect different behavior between dblclick and auxclick, should they be consistent?
I added auxclick to that test just to test the behavior at the request of my code reviewer. I see that safari and firefox both are blocking auxclick events, but we never discussed blocking them in addition to the list of events we have explicitly discussed blocking, which currently includes click, mouseup, mousedown, and dblclick. Is it true that auxclick events are also being blocked on disabled form control elements in firefox/safari? Is there a reason? Are there additional events that I'm not aware of that we need to consider?
Gecko doesn't fire contextmenu either, although it's an allowlist instead of denylist in Gecko, so anything not in that list is blocked. (See https://github.com/whatwg/html/issues/2368#issuecomment-1791524938)
I have no strong opinion, I don't think contextmenu and auxclick is about activation, so I don't see any reason to block them.
Not sure that my point is related to this topic, but I found this behaviour:
Event "dblclick" and "contextmenu" are fired for disabled button in Google Chrome. In Edge only "contextmenu" is fired for disabled button. On Firefox no one of above are fired. On Safari I faced with strange behaviour: seems these events are not fired for disabled button. But there is a case when both of them are fired (https://harelyshau.dev/#/minesweeper). Don't understand the difference.
All tests on MacOS.
With following example:
<button onclick="console.log('left click')" oncontextmenu="event.preventDefault(); console.log('right click')" ondblclick="console.log('double click')" disabled />
Are there any simple workarounds for mouseenter
besides handling events on the disabled control wrapper?
There is a pretty legit case for it like showing a hint explaining why a form control is disabled.
displaying a hint, via tooltip, to describe why a control is disabled is something people are doing - but it's an antipattern that doesn't account for the fact that if the control is disabled, then someone using keyboard wont' be able to focus the control / reveal this tooltip.
Unfortunately, the only workaround I would suggest is a design that is more inclusive, and doesnt' assume that all users can hover over the disabled control.
Event "dblclick" and "contextmenu" are fired for disabled button in Google Chrome. In Edge only "contextmenu" is fired for disabled button.
dblclick is blocked in chromium, which includes chrome and edge. I implemented this due to feedback in this thread: https://github.com/whatwg/html/issues/2368#issuecomment-1791704780
Are there any simple workarounds for mouseenter besides handling events on the disabled control wrapper?
mouseenter should work on disabled form controls. it is fired in chrome based on my testing: https://jsfiddle.net/jarhar/652yamct/4/
Unfortunately, the only workaround I would suggest is a design that is more inclusive, and doesnt' assume that all users can hover over the disabled control.
+1 to making websites accessible
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:
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.