Bug 171105 - Normalizing touch events mouse compatibility event bubbling
Summary: Normalizing touch events mouse compatibility event bubbling
Status: RESOLVED DUPLICATE of bug 151933
Alias: None
Product: WebKit
Classification: Unclassified
Component: UI Events (show other bugs)
Version: Safari 10
Hardware: iPhone / iPad iOS 10
: P2 Enhancement
Assignee: Nobody
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2017-04-21 01:44 PDT by Patrick H. Lauke
Modified: 2018-01-04 12:44 PST (History)
8 users (show)

See Also:


Attachments
Testcase (1.11 KB, text/html)
2018-01-03 18:10 PST, Simon Fraser (smfr)
no flags Details
Test case showing lack of event bubbling (3.24 KB, text/html)
2018-01-04 06:25 PST, Patrick H. Lauke
no flags Details
Click handler on the body (608 bytes, text/html)
2018-01-04 11:27 PST, Simon Fraser (smfr)
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Patrick H. Lauke 2017-04-21 01:44:42 PDT
Currently, a touchscreen tap on an arbitrary (non-focusable, non-actionable) part of a document results in mouse compatibility events (the classic mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click that follow after the touchstart > touchend) on that particular part of the document that do not bubble as expected; likewise, if an element is currently focused (e.g. a <button>) from a previous tap interaction, this tap outside of it on an arbitrary element also does not send the mouseout > mouseleave > blur event sequence to that focused element (although the focus does seem to move away from the element?).

This has been partially documented (through trial and error) here https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html (and I usually mention it in workshops, see https://patrickhlauke.github.io/getting-touchy-presentation/#202)

It's likely that this behavior was introduced (together with the old "Making Elements Clickable" technique, which required authors to add empty event onclick handlers to their elements in order to be able to get the mouse compat events on that element itself - see https://patrickhlauke.github.io/getting-touchy-presentation/#204 - which is a behavior that has since been removed in iOS) for performance optimization reasons back in the early days of iOS' event handling.

However, this heuristic behavior is currently not documented on developer.apple.com, and it causes problems with event delegation approaches. As a real-world example, I recently spent some time adding iOS hacks to Bootstrap's core scripting to enable things such as dropdowns and tooltips closing when a user taps outside of them (see https://github.com/twbs/bootstrap/pull/22426). The hack involved dynamically adding/removing noop mouse handlers to the child elements of <body>, in order to coax iOS/Safari into bubbling the mouse compatibility events.

Admittedly, one could argue that on touch devices one shouldn't rely on event bubbling of mouse compat events, and instead write explicit touchstart/touchend handlers. However, this would make the resulting code far more complex. AND event bubbling as a result of touch happens correctly in all other tested platforms (e.g. Windows 10 Mobile, Android, touch-enabled Windows laptops with Chrome, Firefox).

Is it possible to reconsider iOS' current quirky and undocumented event bubbling for mouse events? If the concern was performance, this may have held true back in the days when the behavior was first devised, but on modern/current iOS devices, I'd posit the perf impact may be minimal (particularly since other browsers on other platforms seem to manage this).

Failing that, it would still be good to get this documented officially somewhere.
Comment 1 Patrick H. Lauke 2017-10-20 02:29:46 PDT
Any chance to at least document this on developer.apple.com?
Comment 2 Radar WebKit Bug Importer 2017-10-20 10:56:51 PDT
<rdar://problem/35097817>
Comment 3 Simon Fraser (smfr) 2018-01-02 13:24:04 PST
Ping to self
Comment 4 Simon Fraser (smfr) 2018-01-03 18:10:38 PST
Created attachment 330428 [details]
Testcase

In this simple testcase, bubbling of the synthetic click works fine.
Comment 5 Patrick H. Lauke 2018-01-04 06:24:52 PST
Simon, that test case works because you already have a click handler attached to the elements. The issue does not manifest itself when you have:

* target element is a link or form control
* target or any ancestor (up to but not including body) has explicit mouse or click handler (even if only empty function)
* target or any ancestor (up to and including document) has cursor:pointer

https://patrickhlauke.github.io/getting-touchy-presentation/#221

The problem with bubbling/delegation happens when you don't have any of the above. For instance, user taps on a <div> but listens for click on the body (as many event delegation frameworks do).

As an example, see https://patrickhlauke.github.io/touch/bubbling/div.html (attached as single file testcase here too)
Comment 6 Patrick H. Lauke 2018-01-04 06:25:26 PST
Created attachment 330463 [details]
Test case showing lack of event bubbling
Comment 7 Patrick H. Lauke 2018-01-04 06:26:39 PST
There are more permutations I tested for this at https://patrickhlauke.github.io/touch/ (under the "iOS event bubbling" section)
Comment 8 Simon Fraser (smfr) 2018-01-04 11:27:57 PST
Created attachment 330475 [details]
Click handler on the body

This testcase shows the bug.
Comment 9 Simon Fraser (smfr) 2018-01-04 11:43:40 PST
This happens because Frame::qualifyingNodeAtViewportLocation() doesn't include the body for some reason.
Comment 10 Simon Fraser (smfr) 2018-01-04 11:44:46 PST
And there's some crazy code that looks at CSS cursor styles to decide which nodes are clickable, which seems bizarre.
Comment 11 Simon Fraser (smfr) 2018-01-04 11:54:03 PST
This is a really old issue, covered by rdar://problem/5844416.

It seems like we're trying to avoid having the tap highlight show up over the entire page, but I think we could fix that without breaking handlers on the body.
Comment 12 Simon Fraser (smfr) 2018-01-04 12:44:53 PST

*** This bug has been marked as a duplicate of bug 151933 ***