Some of the more interesting data points from running my various touch/pointer tests on a variety of devices/browsers. All tests carried out manually, trying to get the cleanest possible results (e.g. getting a “clean” tap without any finger movement). Most testing carried out against this simple event listener test.

Some of these results first appeared in my presentation Getting touchy - an introduction to touch and pointer events, but have since been retested and are now collated here for easier referencing.

See also Peter-Paul Koch’s “Touch table”, where he tests some further aspects beyond the scope of my tests.

Last updated 26 August 2020. See complete change history.

Contents

Mobile/tablet touchscreen activation/tap event order

Browser 1st tap 2nd tap Tap out
Firefox OS 1.1 touchstart > (touchmove)+ > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click touchstart > (touchmove)+ > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
iOS 7.1 / Safari/WebView touchstart > (touchmove)+ > touchend > (mouseenter) > mouseover > mousemove > mousedown > mouseup > click touchstart > (touchmove)+ > touchend > mousemove > mousedown > mouseup > click mouseleave > mouseout
(when tapping to another focusable/activatable element, otherwise none due to iOS’s event delegation/bubbling idiosyncrasy)
Opera Mini 8 / iOS 7.1.1 (Mini mode, Turbo and Wi-Fi consistent with Safari/iOS) mouseover > mousemove > mousedown > mouseup > click mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
(when tapping to another focusable/activatable element, otherwise none)
Android 2.1 (HTC Hero) / “Internet” (WebKit 530.17) touchstart > (touchmove)+ > touchend > mouseover > mousemove > mousedown > mouseup > click touchstart > (touchmove)+ > touchend > mousemove > mousedown > mouseup > click mouseout
Android 2.3.7 / Browser (WebKit 533.1) touchstart > (touchmove)+ > touchend > mouseover > mousemove > mousedown > mouseup > click touchstart > (touchmove)+ > touchend > mousemove > mousedown > mouseup > click mouseout
Android 4.3 / Chrome M34 touchstart > (touchmove)+ > touchend > mouseover > mousemove > mousedown > focus > mouseup > click touchstart > (touchmove)+ > touchend > mousemove > mousedown > mouseup > click mouseout > blur
Android 4.3 / Miren Browser, Maxthon Browser, Dolphin Browser (HD), Browser (WebKit 534.30) mouseover > mousemove > touchstart > (touchmove)+ > touchend > mousedown > mouseup > click mousemove > touchstart > (touchmove)+ > touchend > mousedown > mouseup > click mouseout
Android 6 / Dolphin Browser 11.5 mouseover > mousemove > touchstart > (touchmove)+ > touchend > mousedown > mouseup > click mousemove > touchstart > (touchmove)+ > touchend > mousedown > mouseup > click mouseout
Android 4.3 / UC Browser 9.6 (WebKit 533.1) mouseover > mousemove > touchstart > (touchmove)+ > touchend > mousedown > focus > mouseup > click mousemove > touchstart > (touchmove)+ > touchend > mousedown > mouseup > click mouseout > blur
Android 6 / UC Browser 10.8 mouseover > mousemove > touchstart > (touchmove)+ > touchend > mousedown > focus > mouseup > click mousemove > touchstart > (touchmove)+ > touchend > mousedown > mouseup > click mouseout > blur
Android 4.3 / Opera 19 (Blink) touchstart > (touchmove)+ > touchend > mouseover > mousemove > mousedown > focus > mouseup > click touchstart > (touchmove)+ > touchend > mousemove > mousedown > mouseup > click mouseout > blur
Android 4.3 / Opera Classic touchstart > (touchmove)+ > touchend > mouseenter > mouseover > mousemove > mousedown > focus > mouseup > click touchstart > (touchmove)+ > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Android 4.3 / Firefox 28 touchstart > (touchmove)+ > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click touchstart > (touchmove)+ > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Android 4.3 / Puffin 4.0.1.828 (no touch event support) mouseenter > mouseover > mousemove > mousedown > focus > mouseup > click mousemove > mousedown > mouseup > click mouseleave > mouseout > blur
Android 4.4 / Maxthon Browser HD 4.0.4.1000 touchstart > touchend > mouseover > mousemove > mousedown > focus > mouseup > click touchstart > touchend > mousemove > mousedown > mouseup > click mouseout > blur
BlackBerry PlayBook 2.0 (2.1.0.1917) / Browser (WebKit 536.2) touchstart > mouseover > mousemove > mousedown > touchend > mouseup > click touchstart > mousemove > mousedown > touchend > mouseup > click mouseout
Blackberry (BBOS 10.1) / Browser touchstart > touchend > mouseover > mousemove > mousedown > mouseup > click touchstart > touchend > mousemove > mousedown > mouseup > click mouseout
Windows Phone 8 / IE10 (pointer events, vendor-prefixed) mousemove > MSPointerOver > mouseover > mouseenter > MSPointerDown > mousedown > (MSGotPointerCapture) > focus (if previously focus was somewhere else on page) > MSPointerUp > mouseup > (MSLostPointerCapture) > MSPointerOut > mouseout > mouseleave > focus (if nothing previously focused on page) > click

if moving finger slightly during press:
mousemove > MSPointerOver > mouseover > mouseenter > MSPointerDown > mousedown > (MSGotPointerCapture) > focus > (MSPointerMove > mousemove)+ > MSPointerUp > mouseup > click > (MSLostPointerCapture) > MSPointerOut > mouseout > mouseleave
mousemove > MSPointerOver > mouseover > mouseenter > MSPointerDown > mousedown > (MSGotPointerCapture) > MSPointerUp > mouseup > (MSLostPointerCapture) > MSPointerOut > mouseout > mouseleave > click

if moving finger slightly during press:
mousemove > MSPointerOver > mouseover > mouseenter > MSPointerDown > mousedown > (MSGotPointerCapture) > (MSPointerMove > mousemove)+ > MSPointerUp > mouseup > click > (MSLostPointerCapture) > MSPointerOut > mouseout > mouseleave
blur
Windows Phone 8.1 / IE11 (pointer events) mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > mousedown > (gotpointercapture) > (pointermove > mousemove)+ > pointerup > mouseup > (lostpointercapture) > pointerout > mouseout > pointerleave > mouseleave > focus > click

if moving finger slightly during press:
mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > mousedown > (gotpointercapture) > pointermove > mousemove > focus > (pointermove > mousemove)+ > pointerup > mouseup > (lostpointercapture) > pointerout > mouseout > pointerleave > mouseleave > click
mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > mousedown > (gotpointercapture) > (pointermove > mousemove)+ > pointerup > mouseup > (lostpointercapture) > pointerout > mouseout > pointerleave > mouseleave > click

unable to replicate a tap with slight movement which does not result in a pointercancel
blur
Windows Phone 8.1 Update 1 / IE11 (pointer events + touch events) mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > touchstart > mousedown > (gotpointercapture) > focus > pointermove > mousemove > pointerup > touchend > mouseup > (lostpointercapture) > pointerout > mouseout > pointerleave > mouseleave > click

if moving finger slightly during press:
mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > touchstart > mousedown > (gotpointercapture) > focus > pointermove > mousemove > (pointermove > touchmove > mousemove)+ > pointerup > touchend > mouseup > (lostpointercapture) > pointerout > mouseout > pointerleave > mouseleave > click
(slightly erratic - sometimes only fires pointermove > mousemove without touchmove, sometimes just pointermove > mousemove followed by full pointermove > touchmove > mousemove)
mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > touchstart > mousedown > (gotpointercapture) > pointermove > mousemove > pointerup > touchend > mouseup > (lostpointercapture) > pointerout > mouseout > pointerleave > mouseleave > click

if moving finger slightly during press:
mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > tochstart > mousedown > (gotpointercapture) > (pointermove > touchmove > mousemove)+ > pointerup > touchend > mouseup > click > (lostpointercapture) > pointerout > mouseout > pointerleave > mouseleave
(slightly erratic - sometimes only fires pointermove > mousemove without touchmove, sometimes just pointermove > mousemove followed by full pointermove > touchmove > mousemove)
blur
Windows 10 Mobile Technical Preview (OS version 10.0.12534.59) / Spartan (pointer events + touch events) pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointermove > pointerup > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click > (lostpointercapture) > pointerout > pointerleave
(note all mouse events fired after touchend - also focus and lostpointercapture currently happen differently than on desktop with touch)
pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointermove > pointerup > touchend > mousemove > mousedown > mouseup > click > (lostpointercapture) > pointerout > pointerleave
(note all mouse events fired after touchend - also focus and lostpointercapture currently happen differently than on desktop with touch)
mouseout > mouseleave
(note no blur, even when tapping away to another focusable element)
Windows 10 Mobile / Microsoft Edge 11 (pointer events + touch events) pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointermove > pointerup > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click > (lostpointercapture) > pointerout > pointerleave
(note all mouse events fired after touchend - also focus and lostpointercapture currently happen differently than on desktop with touch)
pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointermove > pointerup > touchend > mousemove > mousedown > mouseup > click > (lostpointercapture) > pointerout > pointerleave
(note all mouse events fired after touchend - also focus and lostpointercapture currently happen differently than on desktop with touch)
mouseout > mouseleave > blur
Windows 10 Mobile / Microsoft Edge 21.10549 (pointer events + touch events) mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > touchstart > mousedown > (gotpointercapture) > focus > pointermove > mousemove > pointerup > touchend > mouseup > click > (lostpointercapture) > pointerout > mouseout > pointerleave > mouseleave
(same as desktop with touchscreen, barring the faked touch events which are disabled on desktop)
mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > touchstart > mousedown > (gotpointercapture) > pointermove > mousemove > pointerup > touchend > mouseup > click > (lostpointercapture) > pointerout > mouseout > pointerleave > mouseleave
(same as desktop with touchscreen, barring the faked touch events which are disabled on desktop)
blur
Android 6.0.1 / Chrome 53 (Dev) (pointer events experimental support + touch events) pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > pointerout > pointerleave > (lostpointercapture) > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > pointerout > pointerleave > (lostpointercapture) > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Android 6.0.1 / Chrome 56 (pointer events + touch events) pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Android 6.0.1 / Samsung Internet for Android 4.0.20-6 touchstart > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click touchstart > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Windows 10 Mobile / Microsoft Edge 25.10586.107.0 (pointer events + touch events) pointerover > pointerenter > pointerdown > touchstart > gotpointercapture > pointermove > pointerup > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click > lostpointercapture > pointerout > pointerleave pointerover > pointerenter > pointerdown > touchstart > gotpointercapture > pointermove > pointerup > touchend > mousemove > mousedown > mouseup > click > lostpointercapture > pointerout > pointerleave mouseout > mouseleave > blur
Amazon Fire OS 5.3.3.0 (Android 5.1.1) / Silk Browser 58.2.6.3029.83.10 (pointer events + touch events) pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
iOS 12.2 / Safari/WebView touchstart > (touchmove)+ > touchend > mouseover > mouseenter > mousemove > mousedown > mouseup > click > mousedown > mouseup > click
(note the odd double firing of events at the end)
touchstart > (touchmove)+ > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave
(when tapping to another focusable/activatable element, otherwise none due to iOS’s event delegation/bubbling idiosyncrasy)
iOS 12.2 / Safari (Experimental WebKit Features - Pointer Events) pointerdown > touchstart > pointermove > pointerup > touchend > pointerup > mouseover > mouseenter > mousemove > mousedown > mouseup > click > mousedown > mouseup > click
(note the odd double firing of events at the end)
pointerdown > touchstart > pointerup > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave
(when tapping to another focusable/activatable element)
Android 6.0.1 / Chrome 74 (pointer events + touch events) pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Android 6.0.1 / Firefox 66.0.2 touchstart > (touchmove)+ > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click touchstart > (touchmove)+ > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Android 6.0.1 / Firefox 68.0a1 (Nightly) pointerover > pointerenter > pointerdown > touchstart > pointerup > pointerout > pointerleave > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click pointerover > pointerenter > pointerdown > touchstart > pointerup > pointerout > pointerleave > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Android 10 / Chrome 77 (pointer events + touch events) pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > (pointermove)+ > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > (pointermove)+ > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Android 10 / Firefox 68.1.1 (with Pointer Events enabled explicitly in about:config) pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
iOS 13.1 / Safari (pointer events + touch events) pointerover > pointerenter > pointerdown > touchstart > gotpointercapture > pointermove > pointermove > pointerup > lostpointercapture > pointerout > pointerleave > touchend > mouseover > mouseenter > mousemove > mousedown > mouseup > click pointerover > pointerenter > pointerdown > touchstart > gotpointercapture > pointermove > pointermove > pointerup > lostpointercapture > pointerout > pointerleave > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave
(when tapping to another focusable/activatable element)

Generally, only a single (“sacrificial”) mousemove is fired as part of the mouse compatibility events, just to rattle any legacy scripts that may be listening for this event.

There is a bug in WebKit (affecting iOS 7.1/Safari and WebView) where mouseenter mouse compatibility event is not being fired correctly - see Bug 128534 - mouseenter mouse compat event not fired when listeners for touch events. This has been fixed in iOS 8

In the touch event model, mouse compatibility events (and final click) are only fired when a single finger is on the touchscreen. As soon as there’s two or more (even outside of the element we’re listening on), only touch events are fired and mouse compatibility events are suppressed (or, in certain browsers like default Android “Browser”, no events are fired at all unless all touches happen on the element with the listener - if, for instance, one finger is touching the screen outside of the element, a tap with a second finger will generate no events at all).

There is a bug in current Blink implementations (noticed in Android/Chrome M34, M35 beta, and related Blink-based browsers like Opera) which also suppresses actual touch events if the first touch happened on a region with no touch handlers - see Issue 363321: No touch are ever events fired if the first finger went down in an area with no handler.

If during the tap there is too much movement of the finger (based on browser-specific threshold), this is considered a gesture rather than a tap, and any mouse compatibility events (including click) are generally not fired.

There is a bug in older versions of Blink and Webkit (noticed in Android/Chrome M34, Android/Opera, Android/Maxthon, Android/Browser) where even the slightest movement during a tap results in touchstart, a single touchmove only, and a touchcancel, with no further events being dispatched. This behavior is inconsistent with other implementations (as noted above, the result is usually touchstart, a few touchmove events, a touchend and then - provided the movement was within the threshold - the mouse compatibility and click events). Android/Chrome M35 (beta) seems to have fixed this bug - see Issue 363319: Swiping over a button with touch event handlers results in a single touchmove then touchcancel.

When testing touch events on desktop using developer tools emulation, note there are currently bugs in the order/type of events being fired:

Note the mix of pointer and touch events in Windows Phone 8.1 Update 1 - see MSDN: The Mobile Web should just work for everyone.

Note that Spartan on current Windows 10 Mobile Technical Preview behaves subtly differently from Spartan on Windows 10 Technical Preview on desktop with touchscreen.

There is some oddity/bug in iOS 12.2, with mousedown, mouseup and click being fired twice when first tapping on the control. The experimental WebKit feature for pointer events also generates some duplicate pointerup events.

For Android, most Chromium-based browsers (Edge, Samsung Internet, Brave) now behave consistently with Chrome.

Mobile/tablet touchscreen with assistive technology event order

Using touch gestures (e.g. swipe left/right, double-tap to activate) and “touch explore”.

Browser Move to button 1st activation 2nd activation Leave button
iOS 7.1 / Safari/WebView + VoiceOver (with and without keyboard, and using “touch explore”) focus touchstart > touchend > mouseenter > mouseover > mousemove > mousedown > blur > mouseup > click touchstart > touchend > mousemove > mousedown > mouseup > click blur
(when moving to another focusable element without activating the control first, otherwise none)

mouseleave > mouseout
(fired after element was already left, if original element was activated, and now another element was activated)
iOS 9.2 / Safari/WebView + VoiceOver (with and without keyboard, and using “touch explore”) focus touchstart > touchend > *mouseover* > mouseenter > mousemove > mousedown > blur > mouseup > click touchstart > touchend > mousemove > mousedown > mouseup > click blur
(when moving to another focusable element without activating the control first, otherwise none)

*mouseout* > mouseleave
(fired after element was already left, if original element was activated, and now another element was activated)
Android 4.3 / Chrome M34 + TalkBack (effectively ChromeVox, same behavior in other WebView/WebKit enabled browsers like Miren, Maxthon, etc) focus

mouseenter > mouseover > (mousemove)+ > focus
(when using “touch explore”)
blur > mousedown > mouseup > click > focus blur > mousedown > mouseup > click > focus blur

(mousemove)+ > mouseleave > mouseout
(when using “touch expore”)
Android 6.0.1 / Chrome 47 + TalkBack none touchstart > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click touchstart > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
(after moving to and activating another focusable element)
Android 4.3 / Firefox 28 + TalkBack none

mousover
(when using “touch explore”)
touchstart > mousedown > focus > touchend > mouseup > click touchstart > mousedown > touchend > mouseup > click blur
(only if element was activated at least once, and only when moving to another focusable element and activating it, otherwise none)

mouseout > blur
(when using “touch explore” - blur also only fired if element was activated at least once, and only when moving to another focusable element and activating it)
Android 6.0.1 / Firefox 47 + TalkBack none

mousover > mouseenter > (mousemove)+
(when using “touch explore”)
touchstart > mousedown > focus > touchend > mouseup > click touchstart > mousedown > touchend > mouseup > click blur
(only if element was activated at least once, and only when moving to another focusable element and activating it, otherwise none)

mouseout > mouseleave blur
(when using “touch explore” - blur also only fired if element was activated at least once, and only when moving to another focusable element and activating it)
Blackberry (BBOS10) / Browser + Screen Reader none touchstart > touchend > mouseover > mousemove > mousedown > mouseup > click touchstart > touchend > mousemove > mousedown > mouseup > click mouseout
(after moving to and activating another focusable element)
Windows Phone 8.1 / IE11 + Narrator none focus > click click blur
(after moving and activating another focusable element)
Windows Phone 8.1 Update 1 / IE11 + Narrator none focus > click click blur
(after moving and activating another focusable element)
Windows 10 Mobile / Microsoft Edge 21.10549 + Narrator (without keyboard) none focus > click click blur
(after moving and activating another focusable element)
Android 6.0.1 / Chrome 53 (Dev) + TalkBack (pointer events experimental support + touch events) none pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > pointerout > pointerleave > (lostpointercapture) > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > pointerout > pointerleave > (lostpointercapture) > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
(after moving and double-tapping another element)
Android 6.0.1 / Chrome 56 + TalkBack (pointer events + touch events) none pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mouseover > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Android 6.0.1 / Samsung Internet for Android 4.0.20-6 + TalkBack none touchstart > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click touchstart > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
(after moving to and activating another focusable element)
Windows 10 Mobile / Microsoft Edge 25.10586.107.0 + Narrator (without keyboard) none focus > click click none
(even after moving and activating another focusable element - however, returning to the button and activating it does fire focus again, so the focus is moved from it, just not reported/blur not fired)
iOS 12.2 / Safari/WebView + VoiceOver (with and without keyboard, and using “touch explore”) none touchstart > touchend > mouseover > mouseenter > mousemove > mousedown > mouseup > click > mousedown > mouseup > click
(note the odd double firing of events at the end)
touchstart > touchend > mousemove > mousedown > mouseup > click none
(even after moving and activating another focusable element)
iOS 12.2 / Safari (Experimental WebKit Features - Pointer Events) + VoiceOver (with and without keyboard, and using “touch explore”) none pointerdown > touchstart > pointerup > touchend > mouseover > mouseenter > mousemove > mousedown > mouseup > click > mousedown > mouseup > click
(note the odd double firing of events at the end)
pointerdown > touchstart > pointerup > touchend > mousemove > mousedown > mouseup > click none
(even after moving and activating another focusable element)
Android 6.0.1 / Chrome 74 + TalkBack none pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Android 6.0.1 / Chrome 74 + TalkBack + keyboard none focus > mousedown > mouseup > click mousedown > mouseup > click blur
(but only when activating certain other controls, not consistently?)
Android 6.0.1 / Firefox 66.0.2 + TalkBack none touchstart > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click touchstart > touchend > mousemove > mousedown > mouseup > click blur
Android 6.0.1 / Firefox 66.0.2 + TalkBack + Keyboard none touchstart > mousedown > focus > touchend > mouseup > click touchstart > mousedown > touchend > mouseup > click blur
Android 6.0.1 / Firefox 68.0a1 (Nightly) + TalkBack none pointerover > pointerenter > pointerdown > touchstart > pointerup > pointerout > pointerleave > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click pointerover > pointerenter > pointerdown > touchstart > pointerup > pointerout > pointerleave > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Android 6.0.1 / Firefox 68.0a1 (Nightly) + TalkBack + Keyboard none touchstart > mousedown > focus > touchend > mouseup > click touchstart > mousedown > touchend > mouseup > click blur
(after activating another control)
Android 10 / Chrome 77 + TalkBack none focus > mousedown > mouseup > click mousedown > mouseup > click blur
(but only when activating certain other controls, not consistently?)
Android 10 / Chrome 77 + TalkBack + keyboard none focus > mousedown > mouseup > click mousedown > mouseup > click blur
(but only when activating certain other controls, not consistently?)
Android 10 / Firefox 68.1.1 (with Pointer Events explicitly enabled in about:config) + TalkBack none touchstart > mousedown > focus > touchend > mouseup > click touchstart > mousedown > touchend > mouseup > click blur
(after activating another control)
Android 10 / Firefox 68.1.1 (with Pointer Events explicitly enabled in about:config) + TalkBack + keyboard none touchstart > mousedown > focus > touchend > mouseup > click touchstart > mousedown > touchend > mouseup > click blur
(after activating another control)
iOS 13.1 / Safari + VoiceOver (with and without keyboard, and using “touch explore”) none pointerover > pointerenter > pointerdown > touchstart > gotpointercapture > pointerup > lostpointercapture > pointerout > pointerleave > touchend > focus > mousedown > mouseup > click pointerover > pointerenter > pointerdown > touchstart > gotpointercapture > pointerup > lostpointercapture > pointerout > pointerleave > touchend > mousedown > mouseup > click none
(even after moving and activating another focusable element)

No assistive technology available (yet) for Firefox OS, Windows Phone 8 or BlackBerry PlayBook.

Moving the focus (swiping left/right and using “touch explore”) won’t always fire a JavaScript focus event – in many cases, focus is only sent as part of the series of events that follow an activation (double-tap). Though perhaps not a problem in practice, it can cause problems for widgets that expect focus (e.g. skip links that are visually hidden initially) - see Bug 1000082 - [AccessFu] When virtual focus is on a button or link, fire actual focus on the element, too, Issue 657157 - Issue with skip links that are visible only on focus.

Mobile/tablet screenreaders are quite liberal in firing additional blur events when an element is activated – presumably in an attempt to prevent focus-specific CSS styles to “stick” after a user tapped on an in-page link or control. Careful if your code assumes blur means the user has moved away from the focusable element, as the AT focus is actually still there.

There are dramatic differences in the events being fired between gesture navigation and “touch explore” on Android.

There is a bug in WebKit (affecting iOS 7.1/Safari and WebView) where mouseenter mouse compatibility event is not being fired correctly - see Bug 128534 - mouseenter mouse compat event not fired when listeners for touch events (fixed in iOS 8).

Currently in Microsoft Edge 21.10549 it is not possible to use Narrator together with a paired keyboard on a Windows 10 Mobile device to navigate the actual web content – it seems the web view itself is not exposed correctly when using TAB / SHIFT+TAB.

There is some oddity/bug in iOS 12.2, with mousedown, mouseup and click being fired twice when first tapping on the control.

First noticed in iOS 12.2 (but likely happened some time ago): focus is now not fired anymore when the button receives VO focus. likewise, leaving the button and activating some other control does not fire any events anymore either. Compare to old iOS 7.1 behaviour.

For Android, latest testing (April 2019) used more appropriate keyboard interaction (ALT+SHIFT plus cursor keys / ENTER to move focus/trigger control) - previous testing may have been limited to just TAB / SHIFT+TAB). Note how both Chrome and Firefox still send different event sequence if TalkBack is operated using gestures and keyboard.

For Android, most Chromium-based browsers (Edge, Samsung Internet, Brave) now behave consistently with Chrome.

Mobile/tablet touch devices with paired keyboard/mouse event order

Browser Move to button 1st activation 2nd activation Leave button
Android 2.1 (HTC Hero) + built-in trackball / “Internet” (WebKit 530.17) mouseover > mousemove mousemove > mousedown > mouseup > click mousemove > mousedown > mouseup > click mouseout
(when moving to another focusable element, otherwise none)
Android 2.3.7 / Browser + mouse none touchstart > touchend > mouseover > mousemove > mousedown > mouseup > click touchstart > touchend > mousemove > mousedown > mouseup > click mouseout
(when clicking somewhere else)
Android 2.3.7 / Firefox 28 + mouse none touchstart > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click touchstart > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
(when clicking somewhere else)
Android 2.3.7 / Opera 12 + mouse none touchstart > touchend > mouseover > mousemove > mousedown > focus > mouseup > click touchstart > touchend mousedown > mouseup > click mouseout > mouseleave > blur
(when clicking somewhere else)
Android 4.3 / Browser + mouse mouseover > (mousemove)+ (mousemove)+ > touchstart > touchend > (mousemove)+ > mousedown > mouseup > click (mousemove)+ > touchstart > touchend > (mousemove)+ > mousedown > mouseup > click (mousemove)+ > mouseout
Android 4.3 / Chrome M34 + mouse mouseenter > mouseover > (mousemove)+ mouseleave > mouseout > touchstart > touchend > mouseenter > mouseover > (mousemove)+ > mousedown > focus > mouseup > click > mousemove mouseleave > mouseout > touchstart > touchend > mouseenter > mouseover > (mousemove)+ > mousedown > mouseup > click > mousemove (mousemove)+ > mouseleave > mouseout
Android 4.3 / Chrome M34 + keyboard focus click click blur
Android 6 / Chrome + mouse mouseover > mouseenter > (mousemove)+ mouseout > mouseleave > touchstart > touchend > mouseover > mouseenter > (mousemove)+ > mousedown > focus > mouseup > click > mousemove mouseout > mouseleave > touchstart > touchend > mouseover > mouseenter > (mousemove)+ > mousedown > mouseup > click > mousemove (mousemove)+ > mouseout > mouseleave
Android 6 / Chrome + keyboard focus click click blur
Android 4.3 / Opera 19 (Blink) + keyboard (no focus indication) focus click click blur
Android 4.3 / Firefox 28 + mouse mouseover > mouseenter > (mousemove)+ mouseleave > touchstart > touchend > mouseenter > (mousemove)+ > mousedown > focus > mouseup > click mouseleave > touchstart > touchend > mouseenter > (mousemove)+ > mousedown > mouseup > click > mousemove (mousemove)+ > mouseout > mouseleave
Android 4.3 / Firefox 28 + keyboard focus click click blur
Android 6 / Firefox 44 + mouse mouseover > mouseenter > (mousemove)+ touchstart > touchend > (mousemove)+ > mousedown > focus > mouseup > click > mousemove touchstart > touchend > (mousemove)+ > mousedown > mouseup > click > mousemove (mousemove)+ > mouseout > mouseleave
Android 6 / Firefox 44 + keyboard focus click click blur
BlackBerry PlayBook 2.0 (2.1.0.1917) / Browser (WebKit 536.2) + mouse mouseover > (mousemove)+ mousedown > mouseup > click mousedown > mouseup > click (mousemove)+ > mouseout
BlackBerry PlayBook 2.0 (2.1.0.1917) / Browser (WebKit 536.2) + keyboard focus click click blur
Blackberry (BBOS 10) / Browser + mouse mouseover > (mousemove)+ mousedown > mouseup > click mousedown > mouseup > click (mousemove)+ > mouseout
Blackberry (BBOS 10) / Browser + keyboard focus click click blur
Windows 10 Mobile / Microsoft Edge + mouse pointermove > mousemove > pointerover > mouseover > pointerenter > mouseenter > (pointermove > mousemove)+ pointerdown > mousedown > focus > pointerup > mouseup > click pointerdown > mousedown > pointerup > mouseup > click pointerout > mouseout > pointerleave > mouseleave
Windows 10 Mobile / Microsoft Edge + keyboard focus click click blur
Android 6.0.1 / Chrome 53 (Dev) + keyboard focus click click blur
Android 6.0.1 / Chrome 53 (Dev) + mouse (pointer events experimental support + touch events) pointerover > pointerenter > mouseover > mouseenter pointerout > pointerleave > mouseout > mouseleave > pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > pointerout > pointerleave > (lostpointercapture) > touchend > pointerover > pointerenter > mouseover > mouseenter > pointermove > mousemove > mousedown > focus > mouseup > click pointerout > pointerleave > mouseout > mouseleave > pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > pointerout > pointerleave > (lostpointercapture) > touchend > pointerover > pointerenter > mouseover > mouseenter > pointermove > mousemove > mousedown > mouseup > click pointerout > pointerleave > mouseout > mouseleave > blur
Android 6.0.1 / Chrome 56 + keyboard focus click click blur
Android 6.0.1 / Chrome 56 + mouse (pointer events + touch events) none pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Android 6.0.1 / Samsung Internet for Android 4.0.20-6 + keyboard focus click click blur
Android 6.0.1 (on Samsung Note) / Samsung Internet for Android 4.0.20-6 + mouse mouseover > mouseenter > (mousemove)+ mousemove > touchstart > touchend > (mousemove)+ > mousedown > focus > mouseup > click mousemove > touchstart > touchend > (mousemove)+ > mousedown > mouseup > click (mousemove)+ > mouseout > mouseleave
Android 6.0.1 (on Google Nexus) / Samsung Internet for Android 5.4 (Beta) + mouse none touchstart > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click touchstart > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Android 6.0.1 / Chrome 59 + mouse (since Chrome 58, new mouse handling, does not mimic touch) none pointerover > pointerenter > mouseover > mouseenter > pointerdown > mousedown > focus > pointermove > mousemove > pointerup > mouseup > click pointerdown > mousedown > pointerup > mouseup > click pointerout > pointerleave > mouseout > mouseleave > blur
iOS 12.2 / Safari/WebView + keyboard (without and with Experimental WebKit Features - Pointer Events) focus click click blur
Android 6.0.1 / Chrome 74 + mouse none pointerover > pointerenter > mouseover > mouseenter > pointerdown > mousedown > focus > (pointermove > mousemove >) pointerup > mouseup > click pointerdown > mousedown > pointerup > mouseup > click pointerout > pointerleave > mouseout > mouseleave > blur
(when clicking away on the page/another element)
Android 6.0.1 / Firefox 66.0.2 + mouse mouseover > mouseenter > (mousemove)+ touchstart > touchend > (mousemove)+ > mousedown > focus > mouseup > click touchstart > touchend > (mousemove)+ > mousedown > mouseup > click (mousemove)+ > mouseout > mouseleave (when moving the mouse out of the button)
blur (when clicking away on the page/another element)
Android 6.0.1 / Firefox 68.0a1 + mouse pointerover > pointerenter > pointermove > mouseover > mouseenter > mousemove (> pointermove > mousemove)+ pointerdown > touchstart > pointerup > pointerout > pointerleave > touchend > pointerover > pointerenter > pointermove > mousemove > mousedown > focus > mouseup > click pointerdown > touchstart > pointerup > pointerout > pointerleave > touchend > pointerover > pointerenter > pointermove > mousemove > mousedown > focus > mouseup > click pointerout > pointerleave > mouseout > mouseleave > blur
(when clicking away on the page/another element)
Android 10 / Chrome 77 + keyboard focus click click blur
Android 10 / Firefox 68.1.1 + keyboard focus click click blur
Android 10 / Chrome 77 + mouse none pointerover > pointerenter > mouseover > mouseenter > pointerdown > mousedown > focus > pointerup > mouseup > click pointerdown > mousedown > pointerup > mouseup > click pointerout > pointerleave > mouseout > mouseleave > blur (after clicking outside of the button)
Android 10 / Firefox 68.1.1 (with Pointer Events enabled in about:config) + mouse pointerover > pointerenter > pointermove > mouseover > mouseenter > mousemove pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > pointerover > pointerenter > pointermove > mousemove > mousedown > focus > mouseup > click pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > pointerover > pointerenter > pointermove > mousemove > mousedown > mouseup > click pointerout > pointerleave > mouseout > mouseleave (after clicking somehwere else, blur)
Android 10 / Firefox Preview 4.3.0 / 38.0.0 + mouse pointerover > pointerenter > pointermove > mouseover > mouseenter > mousemove pointerdown > mousedown > focus > pointerup > mouseup > click > pointermove > mousemove pointerdown > mousedown > pointerup > mouseup > click > pointermove > mousemove pointerout > pointerleave > mouseout > mouseleave
Android 10 / Puffin 7.8.3 + mouse pointerover > pointerenter > mouseover > mouseenter pointerdown > mousedown > focus > pointerup > mouseup > click pointerdown > mousedown > pointerup > mouseup > click pointerout > pointerleave > mouseout > mouseleave (after clicking somehwere else, blur)
iOS 13.1 / Safari/WebView + keyboard focus click click blur
iOS 13.1 / Safari/WebView + mouse none pointerover > pointerenter > pointerdown > touchstart > gotpointercapture > pointermove > pointermove > pointerup > lostpointercapture > pointerout > pointerleave > touchend > mouseover > mouseenter > mousemove > mousedown > mouseup > click pointerover > pointerenter > pointerdown > touchstart > gotpointercapture > pointermove > pointermove > pointerup > lostpointercapture > pointerout > pointerleave > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave
(when clicking on another focusable/activatable element)
iOS 13.6 / Safari/WebView + keyboard
with “Full Keyboard Access” enabled
focus pointerover > pointerenter > pointerdown > touchstart > pointerover > mouseover > pointerenter > mouseenter > gotpointercapture > pointerup > lostpointercapture > pointerout > mouseout > pointerleave > mouseleave > pointerout > pointerleave > touchend > mousedown > mouseup > click pointerove > pointerenter > pointerdown > touchstart > pointerover > mouseover > pointerenter > mouseenter > gotpointercapture > pointerup > lostpointercapture > pointerout > mouseout > pointerleave > mouseleave > pointerout > pointerleave > touchend > mousedown > mouseup > click blur
iPadOS 13.4 / Safari/WebView + mouse pointerover > mouseover > pointerenter > mouseenter pointerdown > mousedown > pointerup > mouseup > click pointerdown > mousedown > pointerup > mouseup > click pointerout > mouseout > pointerleave > mouseleave

Firefox OS (ZTE Open) currently does not support paired bluetooth mouse/keyboard.

Android 2.3.7 only has partial keyboard support. It was not possible to successfully pair a keyboard or mouse with the Android 2.1 HTC Hero test device.

Windows Phone 8 (Nokia Lumia 520) does not support paired bluetooth mouse/keyboard.

Windows 10 Mobile does support paired bluetooth mouse and keyboard - with a mouse, Microsoft Edge behaves exactly the same as Win8.1/IE11 or Windows 10/Microsoft Edge, firing the same events and no faked touch events (even though it does support both touch and pointer events). However, it is not possible to use to keyboard to navigate - using TAB / SHIFT+TAB has no effect. Using a paired keyboard, Microsoft Edge also behaves the same way as its desktop counterpart, only firing focus, click and blur events.

In Android/Chrome 53 (Dev), note how the browser (which supports both touch and pointer events) when paired with a mouse attempts to keep “closure” of the mouse pointer by continuously adding/removing the pointer (firing extra sets of pointerover / pointerenter / pointerout / pointerleave and related compatibility mouse events), compared to Windows 10 Mobile with mouse.

From Android/Chrome 58 onwards, mouse handling behaves like on desktop, and does not mimic touch anymore.

In Firefox 68.0a1 (Nightly), notice the odd pointerout, pointerleave followed milliseconds later by pointerover, pointerenter - like during each tap/activation, the mouse is completely removed from over the element and then reappears.

On Android + mouse, note how Chromium-based browsers all don’t fire any events when mouse pointer is moved over the button (compared to desktop); Firefox and Puffin seem the only main browsers that behave like desktop here (though some of the event sequences are quirky).

For Android, most Chromium-based browsers (Edge, Samsung Internet, Brave) now behave consistently with Chrome.

iOS does not support paired mouse, paired keyboard only works in same situations as on-screen keyboard (e.g. when prompted to enter a web address, enter data in a text input) unless VoiceOver is also activated (in which case it supports full control, but acts the same as regular VoiceOver with touch gestures).

iOS 12.2 (and probably some earlier versions) now supports partial keyboard support even without VoiceOver - navigate using Option+TAB / Option+SHIFT+TAB, activate links and buttons using ENTER (but SPACE doesn’t seem to work for buttons/controls).

iOS 13 introduced support for mouse, but only as a pointing device for Accessibility > Touch > AssistiveTouch. Once paired, the mouse displays a cursor, but acts exactly like touch - it does not hover, or fire any mouseover/mouseenter/mouseleavemouseout (or pointer events equivalent) events. In the pointer events it does fire (on clicking the mouse button), it identifes as a type=="touch" pointer.

iPadOS 13.4 introduced full support for mouse. It now correctly behaves exactly like a desktop mouse. However, it seems to get confused when a mouse “suddenly appears” (e.g. when navigating via touch, and then simply clicking a mouse button that was previously positioned somehwere like a button … it then sends a bizarre combination of touch, pointer and mouse events).

iOS 13.6 (possibly earlier) has an additional setting under “Accessibility > Keyboards” to enable “Full Keyboard Access”. When this is enabled, it is possible to navigate through apps and content (including web content) in a way that’s comparable to desktop keyboard access. Note though that, as noted in the table, the event sequence that is fired includes Pointer Events, Touch Events, and fallback mouse events, rather than just classic keyboard events. This is presumably done as a backwards-compatibility measure to ensure that content that makes assumptions (“it’s a mobile/iPhone, so don’t need to listen to keyboard events…”) still works.

Android 10 / Firefox 68 has some oddities relating to mouse support - see https://bugzilla.mozilla.org/show_bug.cgi?id=1629353. These have been eliminated in the more recent Firefox Preview builds, which use different mouse handling on Android.

Android 10 / Chrome also still does not fully handle mouse hovering - see https://bugs.chromium.org/p/chromium/issues/detail?id=1028118

Desktop with touchscreen event order

Browser 1st tap 2nd tap Tap out
Windows 8 / Chrome 33 (supports touch events) touchstart > (touchmove)+ > touchend > mouseover > mousemove > mousedown > focus > mouseup > click touchstart > (touchmove)+ > touchend > mousemove > mousedown > mouseup > click mouseout > blur
Windows 8 / Firefox 28 mouseover > mouseenter > mousemove > mousedown > focus > (mousemove)+ > mouseup > click mousemove > mousedown > (mousemove)+ > mouseup > click mouseout > mouseleave > blur
Windows 8 / Opera 12 (Presto) mouseover > mousemove > mousedown > focus > mouseup > click mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Windows 8 / Opera 20 (Blink) (supports touch events) touchstart > (touchmove)+ > touchend > mouseover > mousemove > mousedown > focus > mouseup > click touchstart > (touchmove)+ > touchend > mousemove > mousedown > mouseup > click mouseout > blur
Windows 8 / IE11 (supports pointer events) mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > mousedown > (gotpointercapture) > focus > pointermove > mousemove > pointerup > mouseup > (lostpointercapture) > pointerout > mouseout > pointerleave > mouseleave > click
(note click at the end, which is not per Pointer Events specification)
mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > mousedown > (gotpointercapture) > pointermove > mousemove > pointerup > mouseup > (lostpointercapture) > pointerout > mouseout > pointerleave > mouseleave > click
(note click at the end, which is not per Pointer Events specification)
blur
Windows 10 Pro Technical Preview (build 10041) / IE (Edge)
Touch Events disabled, Interop Mouse Events for Touch disabled
mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > mousedown > focus > (gotpointercapture) > pointermove > mousemove > pointerup > mouseup > (lostpointercapture) > click > pointerout > mouseout > pointerleave > mouseleave
(note click in correct sequence as per Pointer Events specification, and focus now before gotpointercapture)
mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > mousedown > (gotpointercapture) > pointermove > mousemove > pointerup > mouseup > (lostpointercapture) > click > pointerout > mouseout > pointerleave > mouseleave
(note click in correct sequence as per Pointer Events specification, and focus now before gotpointercapture)
blur
Windows 10 Pro Technical Preview (build 10041) / IE (Edge)
Touch Events enabled, Interop Mouse Events for Touch disabled
mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > touchstart > mousedown > focus > (gotpointercapture) > pointermove > touchmove > mousemove > pointerup > touchend > mouseup > (lostpointercapture) > click > pointerout > mouseout > pointerleave > mouseleave
(note touchstart and touchend)
mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > touchstart > mousedown > (gotpointercapture) > pointermove > touchmove > mousemove > pointerup > touchend > mouseup > (lostpointercapture) > click > pointerout > mouseout > pointerleave > mouseleave
(note touchstart and touchend)
blur
Windows 10 Pro Technical Preview (build 10041) / IE (Edge)
Touch Events enabled, Interop Mouse Events for Touch enabled
pointerover > pointerenter > pointerdown > touchstart > focus > (gotpointercapture) > pointermove > pointerup > touchend > (lostpointercapture) > mouseover > mouseenter > mousemove > mousedown > mouseup > click > pointerout > pointerleave
(note all mouse events fired after touchend)
pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointermove > pointerup > touchend > (lostpointercapture) > mousemove > mousedown > mouseup > click > pointerout > pointerleave
(note all mouse events fired after touchend)
blur
Windows 10 Pro Technical Preview (build 10049) / Spartan
(same as IE (Edge) with Touch Events enabled, Interop Mouse Events for Touch enabled)
pointerover > pointerenter > pointerdown > touchstart > focus > (gotpointercapture) > pointermove > pointerup > touchend > (lostpointercapture) > mouseover > mouseenter > mousemove > mousedown > mouseup > click > pointerout > pointerleave
(note all mouse events fired after touchend)
pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointermove > pointerup > touchend > (lostpointercapture) > mousemove > mousedown > mouseup > click > pointerout > pointerleave
(note all mouse events fired after touchend)
blur
Windows 10 / Microsoft Edge
(by default touch events and mouse events for touch disabled)
mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > mousedown > focus > (gotpointercapture) > pointermove > mousemove > pointerup > mouseup > (lostpointercapture) > click > pointerout > mouseout > pointerleave > mouseleave mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > mousedown > (gotpointercapture) > pointermove > mousemove > pointerup > mouseup > (lostpointercapture) > click > pointerout > mouseout > pointerleave > mouseleave blur
Windows 10 / Chrome 53 (Dev) (pointer events experimental support + touch events) pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > pointerout > pointerleave > (lostpointercapture) > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > pointerout > pointerleave > (lostpointercapture) > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Chromebook Pixel / Chrome OS 69.0.3497.120 pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Windows 10 / Chrome 77 pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointermove > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointermove > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Windows 10 / Firefox 69 pointerover > pointerenter > pointerdown > touchstart > pointerup > pointerout > pointerleave > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click pointerover > pointerenter > pointerdown > touchstart > pointerup > pointerout > pointerleave > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur
Windows 10 / Edge 44 mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > mousedown > focus > (gotpointercapture) > pointerup > mouseup > click > (lostpointercapture) > pointerout > mouseout > pointerleave > mouseleave mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > mousedown > (gotpointercapture) > pointerup > mouseup > click > (lostpointercapture) > pointerout > mouseout > pointerleave > mouseleave blur
Windows 10 / Edge 78.0.276.11 (beta) pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointerup > (lostpointercapture) > pointerout > pointerleave > touchend > mousemove > mousedown > mouseup > click mouseout > mouseleave > blur

If during the tap there is too much movement of the finger (based on browser-specific threshold), this is considered a gesture rather than a tap. For browsers supporting Touch Events, no mouse compatibility events nor click are generally fired.

In Windows 8.1/IE11, too much movement results in: mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > mousedown > (gotpointercapture) > focus (if not previously focused) > pointerout > mouseout > pointerleave > mouseleave > pointercancel > (lostpointercapture) (note the fact that no pointerup, mouseup nor click are fired).

In Windows 10 / IE (Edge) (build 10041) with Touch Events and Interop Mouse Events for Touch disabled, too much movement results in: mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > mousedown > focus (if not previously focused) > (gotpointercapture) > pointercancel > pointerout > mouseout > pointerleave > mouseleave > (lostpointercapture) (note different order for focus and pointercancel).

In Windows 10 / IE (Edge) (build 10041) with Touch Events enabled, too much movement results in: mousemove > pointerover > mouseover > pointerenter > mouseenter > pointerdown > touchstart > mousedown > focus (if not previously focused) > (gotpointercapture) > (pointermove > mousemove > pointermove > touchmove > mousemove) > pointercancel > touchcancel > pointerout > mouseout > pointerleave > mouseleave > (lostpointercapture) (note the fact that three pointermove, a single touchmove and two mousemove are fired).

In Windows 10 / IE (Edge) (build 10041) with Touch Events and Interop Mouse Events for Touch enabled, too much movement results in: pointerover > pointerenter > pointerdown > touchstart > (gotpointercapture) > pointermove > touchmove > pointercancel > touchcancel > pointerout > pointerleave > (lostpointercapture) (note absence of any focus, even if element was not previously focused).

In addition, in Windows 10 / IE (Edge) (build 10041) with Interop Mouse Events for Touch enabled, touch-action:none does not result anymore in mousemove being fired when moving a touchscreen pointer.

The first preview of Windows 10 / Spartan (build 10049) behaves the same way as IE (Edge) with Touch Events and Interop Mouse Events for Touch enabled.

Windows 10 / Microsoft Edge has Touch events and Mouse events for touch disabled by default.

Desktop with assistive technology event order

Using traditional TAB / SHIFT+TAB / ENTER keyboard navigation. Notice the faked mouse events (particularly in OS X, when activating the test button with CTRL+⌥ ALT+SPACE as prompted by VoiceOver), which are not fired when assistive technology is not present, and are most likely meant for compatibility with sites that assume mouse interactions.

Included primarily as a point of comparison for the more specific case of desktop with touchscreen and assistive technology.

This table also includes classic Opera 12 (Presto) which has a unique spatial navigation feature (also used in Opera-based TV browsers): using SHIFT+cursor keys, this mode allows to set focus with the keyboard not in a sequential tab order, but based on the relative position of elements in the rendered layout (e.g. using SHIFT+ to move to the closest focusable element to the right of the current element). Again, for compatibility with mouse-centric sites/scripts, Opera’s spatial navigation fires the whole range of mouse events when entering and leaving an element.

Browser Move to button 1st activation 2nd activation Leave button
Windows 8 / Chrome 34 + ChromeVox 1.31.4 focus click
(using ENTER or SPACE)

mousedown > mouseup > click
(using ChromeVox+SPACE)
click
(using ENTER or SPACE)

mousedown > mouseup > click
(using ChromeVox+SPACE)
blur
Windows 8 / Chrome 33 + JAWS 15 focus mousedown > mouseup > click mousedown > mouseup > click blur
Windows 8 / Chrome 34 + NVDA 2014.1 focus mousedown > mouseup > click mousedown > mouseup > click blur
Windows 8 / Chrome 33 + Narrator focus click click blur
Windows 8 / IE11 + JAWS 15 focus click click blur
Windows 8 / IE11 + NVDA 2014.1 focus click click blur
Windows 8 / IE11 + Narrator focus click click blur
Windows 8 / IE11 + ZoomText 10.1 focus click click blur
Windows 8 / Firefox 28 + JAWS 15 focus mousedown > mouseup > click mousedown > mouseup > click blur
Windows 8 / Firefox 28 + NVDA 2014.1 focus mousedown > mouseup > click mousedown > mouseup > click blur
Windows 8 / Firefox 28 + Narrator focus click click blur
Windows 8 / Firefox 28 + ZoomText 10.1 focus click click blur
Windows 8 / Opera 20 (Blink) + JAWS 15 focus click click blur
Windows 8 / Opera 20 (Blink) + NVDA 2014.1 focus mousedown > mouseup > click mousedown > mouseup > click blur
Windows 8 / Opera 20 (Blink) + Narrator focus click click blur
Windows 8 / Opera 20 (Blink) + ZoomText 10.1 focus click click blur
Windows 8 / Opera 12 (Presto) + Spatial Navigation
(SHIFT+cursor keys)
focus > mouseenter > mouseover > mousemove click click blur > mouseout > mouseleave
OS X 10.9.2 / Safari 7.0.2 + VoiceOver focus click
(using ENTER or SPACE)

mousedown > mouseup > click
(using CTRL+⌥ ALT+SPACE)
click
(using ENTER or SPACE)

mousedown > mouseup > click
(using CTRL+⌥ ALT+SPACE)
blur
OS X 10.9.2 / Chrome 33 + VoiceOver focus click
(using ENTER or SPACE)

mousedown > mouseup > click
(using CTRL+⌥ ALT+SPACE)
click
(using ENTER or SPACE)

mousedown > mouseup > click
(using CTRL+⌥ ALT+SPACE)
blur
OS X 10.9.2 / Firefox 29 + VoiceOver focus click
(using ENTER or SPACE)

mousedown > blur > mouseup > click
(using CTRL+⌥ ALT+SPACE)
click
(using ENTER or SPACE)

mousedown >mouseup > click
(using CTRL+⌥ ALT+SPACE)
blur
(if not activated or activated using ENTER or SPACE)

none
(if activated using CTRL+⌥ ALT+SPACE)
Windows 10 / Microsoft Edge + NVDA 2015.1 focus click click blur
Windows 10 / Microsoft Edge + JAWS 16 focus click click blur
Windows 10 / Microsoft Edge + Narrator focus click click blur
Windows 10 / Chrome 53 + NVDA 2016.2.1 focus mousedown > mouseup > click mousedown > mouseup > click blur
Windows 10 / Chrome 53 + JAWS 17 focus mousedown > mouseup > click mousedown > mouseup > click blur
Windows 10 / Chrome 53 + Narrator focus click click blur
Windows 10 / Chrome 77 + NVDA 2019.2 focus mousedown > mouseup > click mousedown > mouseup > click blur
Windows 10 / Chrome 77 + JAWS 2018 focus mousedown > mouseup > click mousedown > mouseup > click blur
Windows 10 / Chrome 77 + Narrator (scan mode on) focus > blur > focus blur > focus > blur > focus > mousedown > mouseup > click blur > focus > mousedown > mouseup > click blur
Windows 10 / Firefox 69 + NVDA 2019.2 focus mousedown > mouseup > click mousedown > mouseup > click blur
Windows 10 / Firefox 69 + JAWS 2018 focus click click blur
Windows 10 / Firefox 69 + Narrator (scan mode on) focus > blur > focus focus > mousedown > mouseup > click mousedown > mouseup > click blur
Windows 10 / Edge 44 + NVDA 2019.2 focus pointerdown > mousedown > pointerup > mouseup > click pointerdown > mousedown > pointerup > mouseup > click blur
Windows 10 / Edge 44 + JAWS 2018 focus none (bug) none (bug) blur
Windows 10 / Edge 44 + Narrator (scan mode on) none focus > pointerdown > mousedown > pointerup > mouseup > click pointerdown > mousedown > pointerup > mouseup > click blur
Windows 10 / Edge 79.0.279.0 (Dev) + NVDA 2019.2 focus mousedown > mouseup > click mousedown > mouseup > click blur
Windows 10 / Edge 79.0.279.0 (Dev) + JAWS 2018 focus mousedown > mouseup > click mousedown > mouseup > click blur
Windows 10 / Edge 79.0.279.0 (Dev) + Narrator (scan mode on) focus > blur > focus blur > focus > blur > focus > mousedown > mouseup > click blur > focus > mousedown > mouseup > click blur
Chromebook Pixel / Chrome OS 69.0.3497.120 + ChromeVox focus click click blur
macOS Mojave 10.14.6 / Safari 13.0.1 + VoiceOver focus click
(using ENTER or SPACE)

mousedown > mouseup > click
(using CTRL+⌥ ALT+SPACE)
click
(using ENTER or SPACE)

mousedown > mouseup > click
(using CTRL+⌥ ALT+SPACE)
blur
macOS Mojave 10.14.6 / Chrome 77 + VoiceOver focus click
(using ENTER or SPACE)

mousedown > mouseup > click
(using CTRL+⌥ ALT+SPACE)
click
(using ENTER or SPACE)

mousedown > mouseup > click
(using CTRL+⌥ ALT+SPACE)
blur

When using VoiceOver on OS X, different events are fired depending on how elements are activated – for instance, activating a button with SPACE or ENTER will simply fire click, whereas CTRL+ALT+SPACE (the key combination prompted by VoiceOver) also fires the aforementioned “faked” mouse events.

Similarly, switching between reading and forms mode in NVDA, or scan mode on/off in Narrator, can change the events fired.

Opera 12 (Presto) on Windows, OS X and Opera 20 (Blink) on OS X seem to have no (workable) support for assistive technology. ZoomText 10.1 currently not working at all with Chrome (34).

Recent versions of Firefox (Quantum) don’t seem to work at all with VoiceOver on macOS?

Desktop with touchscreen and assistive technology event order

Using touch gestures (e.g. swipe left/right, double-tap to activate) and “touch explore”, instead of traditional TAB / SHIFT+TAB / ENTER keyboard navigation.

Browser Move to button 1st activation 2nd activation Leave button
Windows 8 / Chrome 33 + JAWS 15 gesture navigation and touch explore (no visible outline / focus indication) none focus > mousedown > mouseup > click blur > focus > mousedown > mouseup > click blur
(only once moved to another focusable element and activated that element)
Windows 8 / Chrome 33 + NVDA 2014.1 gesture navigation and touch explore (no visible outline / focus indication) none focus > mousedown > mouseup > click mousedown > mouseup > click blur
(only once moved to another focusable element and activated that element)
Windows 8 / Chrome 33 + Narrator gesture navigation and touch explore none focus > mousedown > mouseup > click blur > focus > mousedown > mouseup > click blur
(only once moved to another focusable element and activated that element)
Windows 8 / IE11 + JAWS 15 gesture navigation and touch explore (no visible outline / focus indication) none focus > click click blur
(only once moved to another focusable element and activated that element)
Windows 8 / IE11 + NVDA 2014.1 gesture navigation and touch explore (no visible outline / focus indication) none focus > click click none
(not even when moving to/activating another element)
Windows 8 / IE11 + Narrator gesture navigation and touch explore none focus > click click blur
(only once moved to another focusable element and activated that element)
Windows 8 / Firefox 28 + JAWS 15 gesture navigation and touch explore (no visible outline / focus indication) none focus > mousedown > mouseup > click mousedown > mouseup > click blur
(only once moved to another focusable element and activated that element)
Windows 8 / Firefox 28 + NVDA 2014.1 gesture navigation and touch explore (no visible outline / focus indication) none mousedown > focus > mouseup > click mousedown > mouseup > click blur
Windows 8 / Firefox 28 + Narrator gesture navigation and touch explore none focus > mousedown > mouseup > click mousedown > mouseup > click blur
(only once moved to another focusable element and activated that element)
Windows 8 / Opera 20 (Blink) + JAWS 15 gesture navigation (no visible outline / focus indication) [touch explore not working] none focus > mousedown > mouseup > click blur > focus > mousedown > mouseup > click blur
(only once moved to another focusable element and activated that element)
Windows 8 / Opera 20 (Blink) + NVDA 2014.1 gesture navigation and touch explore (no visible outline / focus indication) none focus > mousedown > mouseup > click mousedown > mouseup > click blur
(only once moved to another focusable element and activated that element)
Windows 8 / Opera 20 (Blink) + Narrator gesture navigation [touch explore not working] none focus > mousedown > mouseup > click blur > focus > mousedown > mouseup > click blur
(only once moved to another focusable element and activated that element)
Windows 10 / Microsoft Edge + NVDA 2015.1 gesture navigation and touch explore (no visible outline / focus indication) none focus > click click blur
(only once moved to another focusable element and activated that element)
Windows 10 / Microsoft Edge + JAWS 16 gesture navigation and touch explore (no visible outline / focus indication) none focus > click click blur
(only once moved to another focusable element and activated that element)
Windows 10 / Microsoft Edge + Narrator gesture navigation and touch explore none focus > click click blur
(only once moved to another focusable element and activated that element)
Windows 10 / Chrome 53 + NVDA 2016.2.1 gesture navigation and touch explore (no visible outline / focus indication) none focus > mousedown > mouseup > click mousedown > mouseup > click blur
(only once moved to another focusable element and activated that element)
Windows 10 / Chrome 53 + JAWS 17 gesture navigation/td> none focus > mousedown > mouseup > click blur > focus > mousedown > mouseup > click blur
(only once moved to another focusable element and activated that element)
Windows 10 / Chrome 53 + Narrator gesture navigation and touch explore none focus > mousedown > mouseup > click blur > focus > mousedown > mouseup > click blur
(only once moved to another focusable element and activated that element)
Windows 10 / Chrome 77 + NVDA 2019.2 gesture navigation none focus > mousedown > mouseup > click mousedown > mouseup > click blur
Windows 10 / Chrome 77 + Narrator (scan mode on) gesture navigation none focus > blur > focus > mousedown > mouseup > click blur > focus > mousedown > mouseup > click blur
Windows 10 / Chrome 77 + JAWS 2018 gesture navigation none focus > mousedown > mouseup > click blur > focus**mousedown > mouseup > click blur
Windows 10 / Firefox 69 + NVDA 2019.2 gesture navigation none touchstart > mousedown > focus > touchend > mouseup > click touchstart > mousedown > touchend > mouseup > click blur
Windows 10 / Firefox 69 + Narrator (scan mode on) gesture navigation none focus > touchstart > mousedown > touchend > mouseup > click touchstart > mousedown > touchend > mouseup > click blur
Windows 10 / Firefox 69 + JAWS 2018 gesture navigation none focus > touchstart > mousedown > touchend > mouseup > click touchstart > mousedown > touchend > mouseup > click blur
Windows 10 / Edge 44 + NVDA 2019.2 gesture navigation none focus > pointerdown > mousedown > pointerup > mouseup > click pointerdown > mousedown > pointerup > mouseup > click blur
Windows 10 / Edge 44 + Narrator (scan mode on) gesture navigation none focus > pointerdown > mousedown > pointerup > mouseup > click pointerdown > mousedown > pointerup > mouseup > click blur
Windows 10 / Edge 44 + JAWS 2018 gesture navigation none none (bug) none (bug) none
Windows 10 / Edge 79.0.279.0 (Dev) + NVDA 2019.2 gesture navigation none focus > mousedown > mouseup > click mousedown > mouseup > click blur
Windows 10 / Edge 79.0.279.0 (Dev) + Narrator (scan mode on) gesture navigation none focus > blur > focus > mousedown > mouseup > click blur > focus > mousedown > mouseup > click blur
Windows 10 / Edge 79.0.279.0 (Dev) + JAWS 2018 gesture navigation none focus > mousedown > mouseup > click blur > focus > mousedown > mouseup > click blur
Chromebook Pixel / Chrome OS 69.0.3497.120 + ChromeVox gesture navigation focus mousedown > mouseup > click mousedown > mouseup > click blur
Chromebook Pixel / Chrome OS 69.0.3497.120 + ChromeVox (touch to explore to move to / away from control) pointerover > pointerenter >mouseover > mouseenter > (pointermove > mousemove)+ focus > mousedown > mouseup > click mousedown > mouseup > click pointerout > pointerleave > mouseout > mouseleave

Using a desktop screenreader with a touchscreen and touch gestures (swipe left/right and using “touch explore”, similar to mobile/tablet screenreaders), none of the tested combinations of browser/AT fired a focus event when moving the focus outline to an element – focus is only sent as a result of a double-tap activation.

Opera 12 (Presto) on Windows seems to have no support for assistive technology. Chrome+ChromeVox does not currently support touch swipes/gestures on desktop.

Suppressing 300ms delay for touchscreen interactions

Small series of tests to check which of the “tricks” used to suppress the dreaded 300ms delay (for Touch Events, between touchend and the compatibility mouse events, including click; for Pointer Events, just before the click) actually works in various browsers/OSs. Where known, this also includes the particular version of the browser that added support, with reference to the related bug/announcement. Blank cells mean that a particular trick/technique has no effect and that the 300ms delay is still present. (Note that, to be precise, on iOS the delay is actually 350ms)

Browser <meta name="viewport" content="user-scalable=no"> <meta name="viewport" content="minimum-scale=1,maximum-scale=1"> <meta name="viewport" content="width=device-width"> touch-action:none touch-action:manipulation
iOS 9.3 / Safari/WebView Yes
(iOS 9.3)
  Yes
(iOS 9.3)
Yes
(iOS 9.3 - except if content is zoomed in beyond initial value)
Yes
(iOS 9.3 - only if applied to actual clickable element, see Bug 149854 comment #25)
iOS 10 / Safari/WebView     Yes
(iOS 9.3 - except if content is zoomed in beyond initial value)
  Yes
(iOS 9.3 - only if applied to actual clickable element, see Bug 149854 comment #25)
Android 4.3 / Browser (WebKit 534.30)          
Android 6 / Dolphin 11.5          
Android 6 / UC Browser 10.8     Yes    
Android 6 / Chrome 47 Yes
(Chrome 24+)
Yes
(Chrome 24+)
Yes
(Chrome 32+ - except if content is wider than viewport)
Yes
(Chrome 35+)
Yes
(Chrome 35+)
Android 6 / Opera 4.3 Yes Yes Yes Yes Yes
Android / Firefox 43 Yes Yes Yes
(Firefox 30+)
   
Blackberry (BBOS 10) / Browser Yes
(BBOS 10.1)
Yes
(BBOS 10.3)
Yes
(BBOS 10.3)
   
Firefox OS 2.0
(indirectly tested, so may be inaccurate)
Yes
(Firefox OS 2.0+)
  Yes
(Firefox OS 1.4+)
   
Windows Phone 8.1 / IE 11 Yes
(IE11+)
Yes
(IE11+)
  Yes
(IE10 -ms-touch-action, IE11+ unprefixed)
Yes
(IE10 -ms-touch-action, IE11+ unprefixed)
Windows 10 Mobile / Microsoft Edge Yes Yes Yes Yes Yes
Windows 8.1 / Chrome 37 N/A N/A N/A N/A N/A
Windows 8.1 / Firefox 32 N/A N/A N/A N/A N/A
Windows 8.1 / IE 11       Yes
(IE10 -ms-touch-action, IE11+ unprefixed)
Yes
(IE10 -ms-touch-action, IE11+ unprefixed)
Windows 10 / Microsoft Edge       Yes Yes
Amazon Fire OS 5.3.3.0 (Android 5.1.1) / Silk Browser 58.2.6.3029.83.10 Yes Yes Yes Yes Yes
Android 6.0.1 / Samsung Internet for Android 4.0.20-6 Yes Yes   Yes Yes
Android 6.0.1 / Samsung Internet for Android 5.4.00-70 (Beta) Yes Yes Yes Yes Yes
Android 6.0.1 / Samsung Internet 5.4.00-75     Yes Yes Yes
Android 10 / Chrome 77 Yes
unless user toggled Settings > Accessibility > Force enable zoom
Yes
unless user toggled Settings > Accessibility > Force enable zoom
Yes Yes Yes
Android 10 / Firefox 68.1.1 Yes Yes Yes Yes Yes
Android 10 / Samsung Internet 10.1     Yes Yes Yes
Android 10 / Edge 42 Yes
unless user toggled Settings > Accessibility > Force zoom
Yes
unless user toggled Settings > Accessibility > Force zoom
Yes Yes Yes
iOS 13.1 / Safari/WebView + keyboard   Yes Yes Yes Yes

In short, the suggested approach for all modern browsers is to use a combination of <meta name="viewport" content="width=device-width"> (which you’re likely to be already using anyway for mobile-friendly sites) and, if needed, explicit use of touch-action:manipulation on instant controls (such as links or buttons).

To work around 300ms delay in situations where none of the tricks/techniques are supported - in particular, on older iOS/WebKit - developers will have to resort to explicit JavaScript handling (e.g. listening to touchend - which fires as soon as a touch point on a touchscreen is lifted, and before the 300ms delay - and suppressing mouse compatibility and click events), being mindful of not making naive “touch or mouse” assumptions - see Detecting touch: it’s the ‘why’, not the ‘how’. In most cases, simply dropping in FTLabs’ fastclick (or using a custom event handler like tap.js) should achieve the desired result with minimal effort.

iOS 10 update: Safari now ignores user-scalable=no, minimum-scale=1, maximum-scale=1 and similar - by default, users are always able to zoom. The 300ms optimization that was present in 9.3 for these two cases does not work in Safari anymore. For embedded WebViews, these viewport directives supposedly still work (not tested), so it is possible that in these cases (e.g. for hybrid native/web applications) the optimization will still occur.

iOS 9.3 update: this release finally included some of the common ways in which to suppress the delay (Bug 150604 - Implement viewport-width-based fast-click heuristic, Bug 149968 - Web pages with unscalable viewports shouldn’t have a single tap delay) and Bug 149854 - Implement touch-action: manipulation; for iOS). See WebKit blog: More Responsive Tapping on iOS.

iOS 8 update: it seems that in iOS 8, some heuristics have been included in Safari that prevent the 300ms delay from happening depending on the duration of the touch/tap. Fast taps have 300ms delay (as they’re more likely to be the first of a quick double-tap, presumably), while slightly slower taps (if the delay between touchstart and touchend is above a certain threshold, around 125ms, without too much movement) fire the mouse compatibility events instantly (see Disable FastClick on iOS 8 #262 and my short video iOS 8/Safari 300ms delay heuristics). It seems this only happens in Safari, and not in the generic UIWebView, so other browsers like iOS/Chrome still have the delay regardless of tap speed - however, it’s likely present in WKWebView and will roll into other browsers when they upgrade to this. Update: rather than an explict heuristic, this is more of a side effect of a change in iOS’ gesture recogniser.

Also, in a change from iOS 7, innerHTML now also seems to count as a “content change” (see The iOS event cascade and innerHTML)so my tests will need to be updated, as currently no click event is being fired on them.

iOS/WebKit update (December 2015): all relevant bugs (Bug 150604, Bug 149968) and Bug 149854) are now RESOLVED FIXED. See More Responsive Tapping on iOS. However, no indication has yet been given when these changes will find their way into iOS - all signs are still pointing towards a future iOS 10 release (since Apple don’t historically make such wide-sweeping changes in iOS point releases).

iOS 9.3 Preview (January 2016) now includes the viewport and touch-action:manipulation optimisations.

On Windows 8.1 and Windows 10 on a touchscreen device, Chrome and Firefox do not currently support “double-tap to zoom”, so no 300ms delay is present (hence the “N/A” entries in the table). Internet Explorer and Microsoft Edge, on the other hand, does support “double-tap to zoom”, so touch-optimisations to remove the delay (using touch-action:manipulation) will also be necessary on desktop. Also note that viewport is not supported/ignored on desktop in IE/Edge (with some exception when it comes to “Metro” snap mode), so pages using those viewport directives are still zoomable - “double-tap to zoom” still works in these cases, but cannot be prevented by merely adding viewport.

Samsung Internet for Android’s default delay is considerably lower than 300ms - on average, closer to 190ms.

“Faked” event coordinates

As noted in the “Desktop with assistive technology” and “Desktop with touchscreen and assistive technology” sections, even when not using a traditional pointing device, certain combinations of browser/AT generate “fake” mouse events for compatibility with legacy sites/scripts that hang behavior on traditional mouse events.

As part of an admittedly philosophical discussion on whether or not setting focus with a keyboard or screenreader (jumping from one focusable element to the next, usually in linear tab order) could also be considered an act of “pointing” (and as such should generate pointer events - see Bug 24786 - ACTION-64: Propose a non-normative note re the keyboard compat issue), I tested if these “faked” events, as well as the final click, also carry any coordinates in their event object.

Any combinations of browser/AT not listed in this table do not generate “fake” mouse events, and any traditional focus, click, blur events they do fire do not contain any coordinates in their event object, as would be the case with regular keyboard interactions.

Browser Events fired on activation
(unless otherwise noted)
Coordinates
iOS 7.1 / Safari/WebView + VoiceOver touchstart > touchend > mouseover > mousemove > mousedown > (blur) > mouseup > click

mouseleave > mouseout
(when moving to another focusable/activatable element)
center of element

center of the new element that received focus
(when moving to another focusable/activatable element)
Android 4.3 / Chrome M34 + TalkBack (effectively ChromeVox) only touch explore mouseenter > mouseover > (mousemove)+ > (focus)
(when moving to element using “touch explore”)

(mousemove)+ > mouseleave > mouseout
(when moving away from element using “touch expore”)
screenX/screenY are 0
clientX/clientY and pageX/pageY correspond to finger position
Android 4.3 / Firefox 28 + TalkBack using swipe gestures touchstart > mousedown > (focus) > touchend > mouseup > click center of element
Android 4.3 / Firefox 28 + TalkBack using touch explore mouseover
(when moving to element using “touch explore”)

mouseout
(when moving away from element using “touch expore”)
finger position
Windows 8 / Chrome 34 + ChromeVox 1.34.1 mousedown > mouseup > click
(using ChromeVox+SPACE)
none
Windows 8 / Chrome 34 + JAWS 15 mousedown > mouseup > click none
Windows 8 / Chrome 34 + JAWS 15 gesture navigation mousedown > mouseup > click none
Windows 8 / Chrome 34 + NVDA 2014.1 mousedown > mouseup > click
(using SPACE)
none
Windows 8 / Chrome 34 + NVDA 2014.1 gesture navigation mousedown > mouseup > click none
Windows 8 / Chrome 34 + Narrator gesture navigation mousedown > mouseup > click none
Windows 8 / Firefox 28 + JAWS 15 mousedown > mouseup > click center of element
Windows 8 / Firefox 28 + JAWS 15 gesture navigation mousedown > mouseup > click center of element
Windows 8 / Firefox 28 + NVDA 2014.1 mousedown > mouseup > click center of element
Windows 8 / Firefox 28 + NVDA 2014.1 gesture navigation mousedown > mouseup > click center of element
Windows 8 / Firefox 28 + Narrator touchscreen mousedown > mouseup > click center of element
Windows 8 / Opera 20 (Blink) + Narrator gesture navigation mousedown > mouseup > click none
Windows 8 / Opera 20 (Blink) + JAWS 15 gesture navigation mousedown > mouseup > click none
Windows 8 / Opera 20 (Blink) + NVDA 2014.1 mousedown > mouseup > click none
Windows 8 / Opera 20 (Blink) + NVDA 2014.1 gesture navigation mousedown > mouseup > click none
Windows 8 / Opera 12 (Presto) Spatial Navigation focus > mouseenter > mouseover > mousemove
(when focusing)

click
(when activating)

mouseout > mouseleave > blur
(when leaving)
top left corner of element
Windows Phone 8.1 Update 1 / IE11 + Narrator focus > click none
Windows 10 Mobile (Insider Preview 10149) / Microsoft Edge + Narrator click none
Windows 10 / Microsoft Edge + NVDA 2015.1 click none
Windows 10 / Microsoft Edge + JAWS 16 click none
Windows 10 / Microsoft Edge + Narrator click none

One interesting result which is not included in this table (as it’s likely a bug): even for traditional keyboard navigation (with and without Narrator), IE11 passes along coordinates in the event object for the click event when an element is activated. However, these coordinates are meaningless - see Bug 856583: IE11 click event has strange/illogical screenX/screenY, clientX/clientY, pageX/pageY coordinates:

  • using ENTER screenX/screenY are 0
  • using SPACE screenX/screenY are screen coordinates of top left corner of the browser viewport
  • clientX/clientY and pageX/pageY contain negative values, the inverse of the coordinates of the top left corner of browser viewport