whatwg / html

HTML Standard
https://html.spec.whatwg.org/multipage/
Other
8.18k stars 2.71k forks source link

Mouse events & disabled form controls #2368

Open jakearchibald opened 7 years ago

jakearchibald commented 7 years ago

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.

annevk commented 7 years ago

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

tabatkins commented 7 years ago

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

annevk commented 7 years ago

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

jakearchibald commented 7 years ago

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

RByers commented 7 years ago

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?

bzbarsky commented 7 years ago

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

rniwa commented 6 years ago

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 commented 6 years ago

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.

jakearchibald commented 6 years ago

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.

rniwa commented 6 years ago

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

jakearchibald commented 6 years ago

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

jakearchibald commented 6 years ago

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

TimothyGu commented 5 years ago

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.

domenic commented 5 years ago

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.

saschanaz commented 5 years ago

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

eps1lon commented 5 years ago

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.

jgraham commented 3 years ago

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.

saschanaz commented 2 years ago

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.

annevk commented 2 years ago

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

josepharhar commented 2 years ago

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.

josepharhar commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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

domenic commented 1 year ago

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.

josepharhar commented 1 year ago

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.

saschanaz commented 1 year ago

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!

ab-pm commented 1 year ago

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.

josepharhar commented 1 year ago

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 commented 1 year ago

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.

saschanaz commented 1 year ago

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 commented 1 year ago

Just skimmed UI Events spec and it seems auxclick should also be included in the list.

saschanaz commented 1 year ago

BTW, having this and #5886 together really doesn't help, can we close either one?

ab-pm commented 1 year ago

Have you tried listening to pointerdown or pointerup instead? Have you tried adding pointer-events:none to the button?

Thanks for the suggestions!

This doesn't change my expectation though that focus should not change without a mousedown event, which is traditionally used to prevent blurring.

josepharhar commented 1 year ago

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.

josepharhar commented 1 year ago

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.

saschanaz commented 1 year ago

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

saschanaz commented 10 months ago

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?

josepharhar commented 10 months ago

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?

saschanaz commented 10 months ago

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.

harelyshau commented 10 months ago

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 />

DzmVasileusky commented 7 months ago

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.

scottaohara commented 7 months ago

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.

josepharhar commented 7 months ago

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