html icon indicating copy to clipboard operation
html copied to clipboard

Mouse events & disabled form controls

Open jakearchibald opened this issue 8 years ago • 43 comments

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.

jakearchibald avatar Feb 17 '17 10:02 jakearchibald

Someone needs to define "hit testing" basically. That's the root of all these issues.

annevk avatar Feb 17 '17 10:02 annevk

??? 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.

tabatkins avatar Feb 17 '17 18:02 tabatkins

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).

annevk avatar Feb 18 '17 16:02 annevk

@RByers @jacobrossi any insight into where this weird behaviour comes from?

jakearchibald avatar Feb 20 '17 12:02 jakearchibald

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?

RByers avatar Feb 23 '17 14:02 RByers

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

bzbarsky avatar Mar 16 '17 15:03 bzbarsky

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

rniwa avatar Sep 06 '18 07:09 rniwa

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.

rniwa avatar Sep 06 '18 07:09 rniwa

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.

jakearchibald avatar Sep 06 '18 07:09 jakearchibald

You mean pointer events? I don't see any mousemove or mousedown on Chrome 71.0.3544.2

rniwa avatar Sep 06 '18 09:09 rniwa

I'm getting both mouse & pointer events in 68 and 71.

jakearchibald avatar Sep 06 '18 11:09 jakearchibald

@rniwa apologies. It only fires mouse events with the "Experimental web platform" flag.

jakearchibald avatar Sep 06 '18 12:09 jakearchibald

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.

TimothyGu avatar Oct 14 '19 05:10 TimothyGu

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.

domenic avatar Oct 14 '19 05:10 domenic

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!)

saschanaz avatar Oct 14 '19 06:10 saschanaz

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.

eps1lon avatar Oct 14 '19 06:10 eps1lon

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.

jgraham avatar Jun 23 '21 12:06 jgraham

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.

saschanaz avatar Dec 09 '21 13:12 saschanaz

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).

annevk avatar Dec 09 '21 13:12 annevk

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.

josepharhar avatar Feb 03 '22 19:02 josepharhar

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.

josepharhar avatar Jun 24 '22 17:06 josepharhar

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

josepharhar avatar Oct 04 '22 23:10 josepharhar

I filed an intent to ship in chromium: https://groups.google.com/a/chromium.org/g/blink-dev/c/9i0H0J0BzE4

josepharhar avatar Oct 15 '22 22:10 josepharhar

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.

domenic avatar Mar 03 '23 03:03 domenic

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.

josepharhar avatar Mar 03 '23 04:03 josepharhar

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.

https://wpt.fyi/results/html/semantics/disabled-elements/event-propagate-disabled.tentative.html?label=experimental&label=master&aligned is live too!

saschanaz avatar Mar 04 '23 10:03 saschanaz

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.

ab-pm avatar Oct 11 '23 19:10 ab-pm

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?

josepharhar avatar Nov 02 '23 20:11 josepharhar

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.

josepharhar avatar Nov 02 '23 20:11 josepharhar

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

saschanaz avatar Nov 02 '23 20:11 saschanaz