Re: mouseenter, mouseleave, and touch compat/interop

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 Wednesday, 11 February 2015 01:06:21 UTC