touchend (not considered a "clean" tap) • too many touchmove events on activatable elements can lead to touchcancel (in old Chrome/Browser versions) • not all browsers consistently send touchmove • differences in focus / blur and some mouse compatibility events (e.g. mouseenter / mouseleave ) • some events may only fire when tapping away to another focusable element (e.g. blur ) some browsers outright weird...
window) { /* browser supports touch events but user is not necessarily using touch (exclusively) */ /* it could be a mobile, tablet, desktop, fridge ... */ }
fine) { /* primary input has fine pointer precision... so make all buttons/controls small? */ } @media (hover: hover) { /* primary input has hover...so we can rely on it? */ } /* pointer and hover only check "primary" input, but what if there's a secondary input that lacks capabilities? */
coarse) { /* at least one input has coarse pointer precision... provide larger buttons/controls for touch */ } /* test for *any* "least capable" inputs (primary or not) */ Limitation: [mediaqueries-4] any-hover can't be used to detect mixed hover and non-hover capable pointers. Also, pointer / hover / any-pointer / any-hover don't cover non-pointer inputs (e.g. keyboards) – always assume non-hover-capable inputs @media (any-hover: none) { /* at least one input lacks hover capability... don't rely on it or avoid altogether */ }
*/ if ('ontouchstart' in window) { // set up event listeners for touch foo.addEventListener('touchend', ...); ... } else { // set up event listeners for mouse/keyboard foo.addEventListener('click', ...); ... }
// set up event listeners for touch foo.addEventListener('touchend', ...); // set up event listeners for mouse/keyboard foo.addEventListener('click', ...); /* but this would fire our function twice for touch? */ patrickhlauke.github.io/touch/tests/event-listener_naive-event-doubling.html
// set up event listeners for touch foo.addEventListener('touchend', function(e) { // prevent compatibility mouse events and click e.preventDefault(); ... }); // set up event listeners for mouse/keyboard foo.addEventListener('click', ...); patrickhlauke.github.io/touch/tests/event-listener_naive-event-doubling-fixed.html
click / mouse events: • "mobile optimised" <meta name="viewport" content="width=device-width"> • add touch-action:manipulation in CSS (part of Pointer Events) • use helper library like fastclick.js for older browsers • make your viewport non-scalable...
readonly attribute float radiusX; readonly attribute float radiusY; readonly attribute float rotationAngle; readonly attribute float force ; }; force : value in range 0 – 1 . if no hardware support 0 (some devices, e.g. Nexus 10, "fake" force based on radiusX / radiusY )
, -ms-touch-action ) • unprefixed in IE11 (but prefixed versions mapped for compatibility) • event order (when click is fired) incorrect in IE10/IE11 see W3C Pointer Events WG mailing list - Jacob Rossi (Microsoft)
> pointerdown > touchstart > pointerup > touchend > mouseover > mouseenter > mousemove > mousedown > focus > mouseup > click > pointerout > pointerleave Specific order of events is not defined in the spec in this case – will vary between browsers... only problem if handling both pointer events and mouse events (which is nonsensical)
navigator.maxTouchPoints > 0 ) { /* device with a touchscreen */ } if ( navigator.maxTouchPoints > 1 ) { /* multitouch-capable device */ } "you can detect a touchscreen" ...but user may still use other input mechanisms
have to replicate something similar... */ ar points = []; switch (e.type) { case ' pointerdown ': /* add to the array */ break; case ' pointermove ': /* update the relevant array entry's x and y */ break; case ' pointerup ': case ' pointercancel ': /* remove the relevant array entry */ break; }
{ readonly attribute long pointerId; readonly attribute long width; readonly attribute long height; readonly attribute float pressure ; readonly attribute float tangentialPressure; readonly attribute long tiltX; readonly attribute long tiltY; readonly attribute long twist; readonly attribute DOMString pointerType; readonly attribute boolean isPrimary; } pressure : value in range 0 – 1 . if no hardware support, 0.5 in active button state, 0 otherwise