Touch and mouse with hover effects in a web browser

From Exterior Memory
Revision as of 23:35, 16 March 2014 by MacFreek (Talk | contribs) (First draft)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Problem

I use a hover effect on my website for certain links. On iPads and other touch-based devices, this causes the user to press a link twice: first to add the hover effect, and a second time to actually follow the link. This is undesired.

Background

In order to simulate a mouse, browsers such as Webkit mobile fire the following events if a user touches and releases a finger on touch screen (like iPad) (source: Touch And Mouse on html5rocks.com):

  1. touchstart
  2. touchmove
  3. touchend
  4. (300ms delay, where the browser makes sure this is a single tap, not a double tap)
  5. mouseover
  6. mouseenter
  7. mousemove
  8. mousedown
  9. mouseup
  10. click

The hover effect on my website is be different for mouse and touch (For mouse, it uses mouseenter and mouseleave. For touch it uses touchstart and click). The mouse events fired by Webkit and other browsers interferes with this behaviour. What's worse, if the mouseover changes the content or DOM state (and the hover effect does so in my case), the click event is never fired, as explained on Safari Web Content Guide - Handling Events - One-Finger Events, figure 6.4.

Unrelated to this problem, there is a 300 mus

Question

What HTML and JavaScript can be used to support a proper hover effect on links for both touch as well as mouse?

Thus:

  • It should work with devices that support both touch and mouse.
  • No browser detection.
  • A link is followed after a single click. No need to click a second time.
  • Ideally, the hover effect is also shown after a touch event.
  • It works across multiple browsers.
  • As much support for native events, no preventDefault() if possible. In particular, a touch can be cancelled by moving your finger.

Non-Solutions

The following are non-solutions:

...

  • emulate the click event in touchend
  • set a variable in touchend that prevents state changes in subsequent mouse events, and reset that variable

Solution

  • Add hover effects on touchstart and mouseenter.
  • Remove hover effects on mouseleave, touchmove and click.

Note that there is no action on touchend!

This clearly works for mouse events: mouseenter and mouseleave (slightly improved versions of mouseover and mouseout) are fired.

If the user actually clicks a link, the hover effect is also removed. This ensure that it is removed if the user presses the back button in the web browser.

This also works for touch events: on touchstart the hover effect is added. It is not removed on touchend. It is added again on mouseenter, and since this causes no content changes (it was already added), the click event is also fired, and the link is followed without the need for the user to click again!

The 300ms delay that a browser has between a touchstart event and click is actually put in good use because the hover effect will be shown during this short time.

If the user decides to cancel the click, a move of the finger will do so just as normal. Normally, this is a problem since no mouseleave event is fired, and the hover effect remains in place. Thankfully, this can easily be fixed by removing the hover effect on touchmove.

That's it!