Touch Events & Pointer Events
debt(d7/e3/b3/t7)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints list Lighthouse and Chrome DevTools, and automated is marked 'no'. Lighthouse can flag non-passive listeners as a performance audit, but this is a specialist audit tool that only surfaces issues during profiling or an explicit audit run — not during normal development or standard linting. The jank and 300ms delay are only noticeable at runtime on actual touch devices.
Closest to 'simple parameterised fix' (e3). The quick_fix describes switching to Pointer Events API and adding touch-action CSS — this is a targeted refactor within affected components. It's more than a single-line swap because event handler names change and touch-action CSS must be added to relevant elements, but it doesn't span the entire codebase if touch listeners are reasonably localized.
Closest to 'localised tax' (b3). Applies only to web contexts with touch/pointer handling code. Components with custom gesture handling pay the tax (incomplete handling, passive listener boilerplate), but the rest of the codebase is unaffected. It doesn't impose a system-wide architectural cost.
Closest to 'serious trap' (t7). The misconception field explicitly states that developers believe 'click events work fine on mobile,' which directly contradicts reality — click has a 300ms delay and doesn't fire for swipe gestures. This is a cross-platform behavioral difference (desktop vs. mobile) that a competent developer familiar with desktop web development would naturally get wrong. The non-passive listener scroll-blocking behavior compounds the trap.
Also Known As
TL;DR
Explanation
Touch Events (legacy): touchstart, touchmove, touchend — each has a touches list of all active contact points. Pointer Events (modern): pointerdown, pointermove, pointerup, pointercancel — single event model for mouse (pointerId=1), touch (multiple pointers), and stylus. Use Pointer Events in new code. Touch performance: passive event listeners (addEventListener('touchstart', fn, {passive: true})) tell the browser the listener won't call preventDefault(), allowing scroll optimisation without waiting for JS. 300ms click delay: eliminated in modern browsers; add touch-action: manipulation to CSS as a belt-and-braces fix.
Common Misconception
Why It Matters
Common Mistakes
- Non-passive touchstart/touchmove listeners — causes 50-200ms scroll delay.
- Using touchstart without touchend and touchcancel — incomplete gesture handling.
- Different code paths for touch and mouse — use Pointer Events for unified handling.
- Tap targets under 44x44px — too small for reliable touch input.
Code Examples
// Blocks scroll — browser waits for JS before scrolling:
document.addEventListener('touchstart', handleTouch);
// Default: {passive: false} — browser must wait for JS
// Separate mouse and touch handlers — maintenance burden:
element.addEventListener('click', handler);
element.addEventListener('touchstart', handler);
// Pointer Events — unified, immediate:
document.addEventListener('pointerdown', e => {
if (e.pointerType === 'touch') handleTouch(e);
else handleMouse(e);
});
// Passive scroll listener — does not block scrolling:
document.addEventListener('touchmove', handleMove, { passive: true });
/* CSS — eliminate 300ms delay: */
/* button, a { touch-action: manipulation; } */
// Multi-touch:
document.addEventListener('pointerdown', e => {
activePointers.set(e.pointerId, e); // Track each finger
});