Re: mouseenter, mouseleave, and touch compat/interop

Jacob,
I realized it's not completely obvious what semantics we want here.
 mouseenter/mouseleave are normally tied to matching :hover state exactly.
In chromium we set :hover shortly after touchstart, and clear it on (or
shortly after) touchend, which is very different from the WebKit-style
synthetic mouse event behavior.

So in this scenario, we have to choose one of the following:

1) Break the connection between :hover and mouseenter/mouseleave for tap
gestures.  Fire the mouseenter/mouseleave events consistent with the
existing mouse event sequence, i.e.:
touchstart, touchend, mouseenter, mouseover, mousemove, mousedown, mouseup,
click
This is probably the most compatible thing, so probably what we want.  But
it turns out it may be some non-trivial implementation work for us (since
today our mouseenter/mouseleave logic is tied to setting/clearing of
:hover).

2) Change our :hover behavior.  I know this is an area you and I have
discussed as needing interoperability improvement.  Is now the time to talk
about this in detail perhaps?

3) Break the mouse event sequence (but keep the mouseenter/leave :hover
connection), so it might sometimes look something like this on
dwell-then-drag gesture that normally doesn't fire any mouse events:
touchstart, mouseenter, touchmove, mouseleave, touchend
or like one of these (depending on timing) on a tap:
touchstart, mouseenter, touchend, mouseover, mousemove, mousedown, mouseup,
click, mouseleave
touchstart, touchend, mouseenter, mouseover, mousemove, mousedown, mouseup,
click, mouseleave

I'd love to hear more about what exactly IE does when using WebKit-style
mouse event behavior.  This seems even more complicated when you take
pointer events in the mix (presumably the pointer->mouse event mapping gets
disconnected).

Thanks,
  RIck

On Tue, Feb 17, 2015 at 5:34 PM, Rick Byers <rbyers@google.com> wrote:

> For the record, we agreed
> <http://www.w3.org/2015/02/17-touchevents-minutes.html> that we should
> try to fix the bug
> <https://code.google.com/p/chromium/issues/detail?id=457497> with
> mouseenter/mouseleave not being fired in Chrome (Jacob says it looks like
> it shouldn't be that risky).  Mustaq is aiming to land a fix in Chrome 43
> and will follow up here once it's available in Canary for website compat
> testing.
>
> Rick
>
> On Tue, Feb 10, 2015 at 8:05 PM, Rick Byers <rbyers@google.com> wrote:
>
>> On Thu, Feb 5, 2015 at 10:36 AM, Jacob Rossi <Jacob.Rossi@microsoft.com>
>> wrote:
>>
>>>  I’ve mentioned before that we’re exploring firing the Touch Event
>>> spec’s model for compatibility mouse events so we’re more interoperable.
>>> In doing so, we uncovered a nasty interoperability issue that’s quite
>>> frustrating.  This one takes a minute to explain, so grab a cup of coffee
>>> and bear with me. :-)
>>>
>>>
>>>
>>> Safari implements a proprietary heuristic for detecting hover menus.
>>> It works something like this on a simple tap:
>>>
>>>
>>>
>>> 1.       Fire touch events
>>>
>>> 2.       Fire mouse events for hover (e.g. mouseover/enter) , down, up
>>>
>>> 3.       If “page content changes” (insert undocumented proprietary
>>> heuristic), then persist the hover on the node & suppress firing the click
>>> event    [1]
>>>
>>> 4.       If not, then fire events to remove hover and fire click
>>>
>>>
>>>
>>> The heuristic is attempting to detect a hover menu intended for mouse,
>>> which enables a “tap to hover menu, tap again to click” type of experience
>>> when it works.  However, sometimes the heuristic has a false positive
>>> causing non-hover-menus to require multiple taps to get a click event on
>>> something.  This leads web developers to try to work around the heuristic.
>>>
>>>
>>>
>>> Here’s a common pattern we’ve found on popular sites like Netflix:
>>>
>>>
>>> http://stackoverflow.com/questions/3038898/ipad-iphone-hover-problem-causes-the-user-to-double-click-a-link/22444819#22444819
>>>
>>>
>>>
>>> In summary, it looks like:
>>>                 mouseenter – open hover menu
>>>                 mouseleave – close hover menu
>>>
>>>                 click – toggle hover menu
>>>
>>>
>>>
>>> For a mouse in Safari, the menu opens when you mouse over and closes
>>> when you click something or move your mouse out. Cool.
>>>
>>> For touch, the menu opens after you tap. The mouseenter causes “page
>>> content changes” in step #2 above. Thus mouseleave and click do not fire.
>>> Therefore the menu stays open.  You can tap again to click an item in the
>>> menu. Cool.
>>>
>>>
>>>
>>> Now, consider a browser that does not implement Safari’s non-standard
>>> heuristic.  In this case, Blink/Gecko follow the Touch Events spec [2]
>>> essentially and do this:
>>>
>>> 1.       Fire touch events
>>>
>>> 2.       Fire mousemove
>>>
>>> a.       (While not explicitly mentioned in the spec, this causes a
>>> mouseover to fire)
>>>
>>> 3.       Fire mouse down, up
>>>
>>> 4.       Fire click
>>>
>>>
>>>
>>> Note, like Safari, this means hover state essentially persists on the
>>> node until you tap another node (which fires a new mousemove, which removes
>>> hover from the old node and applies it to the new). The Touch Events spec
>>> does not, however, explicitly state when hover related events, like
>>> over/out/enter/leave occur. It’s just assumed the mousemove causes it
>>> (grrr…the spec should say this!).
>>>
>>
>> Agreed, please submit a PR for this.  I've always taken it for granted
>> that over/out/enter/leave events should ALWAYS be consistent with mousemove
>> (any time you get a move with a new target, they're fired appropriate), but
>> stating that explicitly doesn't hurt.
>>
>> Still with me? :-) Here’s the rub:  Blink doesn’t fire
>>> mouseenter/mouseleave for touch, only mouseover/mouseout (I didn’t have a
>>> Firefox mobile device handy to test, but I assume it’s similar).  This is
>>> despite the fact that Blink does support mouseenter/mouseleave for mouse
>>> input. [3]
>>>
>>
>> This is simply a bug from when mouseenter/mouseleave events were first
>> implemented.  I only noticed this recently from code inspection (I added a FIXME
>> in the code
>> <https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/core/page/EventHandler.cpp&q=EventHandler.cpp%20mouseenter&sq=package:chromium&type=cs&l=2659>
>> but failed to file a bug - just filed one now <http://crbug.com/457497>).
>>
>>
>>>  It turns out this is a critical detail missing from the spec.  Because
>>> if you make the mistake we did, which was to fire mouseenter/mouseleave
>>> when processing the mousemove for touch, then the above code I described
>>> breaks as you get:
>>>
>>>
>>>
>>> 1.       Fire touch events
>>>
>>> 2.       Fire mousemove
>>>
>>> a.       Fire mouseover
>>>
>>> b.      Fire mouseenter – open hover menu
>>>
>>> 3.       Fire mouse down, up
>>>
>>> 4.       Fire click – hover menu toggles
>>>
>>>
>>>
>>> So the hover menu opens and then immediately closes.  Not cool.
>>>
>>>
>>>
>>> tl;dr – for best compatibility, browsers need to choose either:
>>>                 (a) try to reverse engineer Safari’s heuristic  (not
>>> likely to be very interoperable)
>>>
>>>                 (b) do not implement mouseenter/mouseleave for touch
>>>
>>
>> Crap crap crap crap.
>>
>> How prevalent is this pattern?  I.e. how much pain would we cause by
>> fixing our bug in blink?
>>
>> The only pragmatic solution I can see (should we end up shipping the
>>> Touch Event model for mouse events in IE) is B.  It should be noted that
>>> I’ve tried to get the Safari engineers to share details about the
>>> heuristic, but they have declined.
>>>
>>>
>>>
>>> I think this critical detail (not firing mouseenter/mouseleave for
>>> touch) should probably be added as errata to the current spec. WDYT?
>>>
>>
>> If we think this is what's best for the web (i.e. enough people have
>> already taken a dependency on our bug), then that's OK with me (although as
>> Patrick says - perhaps that's part of TEv2, not TEv1 errata).  It's not
>> very rational though.  Is there a better pattern we could advocate for?
>> What about combining this with some sort of more explicit signal of
>> developer intent (like your aria-hasrole=popup)?  Ideally we'd have a path
>> where browser's default behavior can be one (confusing) way for compat, but
>> we have clear explicit APIs that could be implemented by all browsers to
>> get developers out of this mess.
>>
>>  -Jacob
>>>
>>>
>>>
>>> [1]
>>> https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html
>>>
>>> [2] http://www.w3.org/TR/touch-events/#mouse-events
>>>
>>> [3] https://status.modern.ie/dom3mouseentermouseleaveevents
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>
>>
>

Received on Friday, 6 March 2015 17:21:12 UTC