mouseenter, mouseleave, and touch compat/interop

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

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]

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

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?

-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 Wednesday, 4 February 2015 23:36:35 UTC