JavaScript Pointer Events Table

PieceWhat it doesField note
pointerdown / move / upOne event modelMouse, touch, pen UNIFIED - write once, every input works
e.pointerTypemouse / touch / penBranch on input type without separate event systems
setPointerCapture(el)Lock the pointerDrags continue OUTSIDE the element - slider fix
e.pointerIdMulti-touch identityTrack each finger separately - maps and multi-drag
pointercancelSystem stole itBrowser took over (scroll) - clean up, don't fight
touch-action: noneLet pointer events liveWithout it, the browser swallows moves for scrolling
click still firesCompatibility layertap = click - simple taps need NO pointer events
e.isPrimaryThe first fingerIgnore non-primary for single-pointer logic
Reference: the MDN Pointer Events reference. Pointer Events UNIFY mouse, touch and pen into one event model: pointerdown/move/up with e.pointerType telling you which - write the interaction once, every input works. The two power-ups: setPointerCapture keeps drags alive outside the element (the slider-past-its-edge fix) and e.pointerId tracks each finger for multi-touch. Bottom line: the browser swallows pointer moves for scrolling unless touch-action: none opts the element out, pointercancel means the system took over (clean up), and simple taps still fire click - pointer events are for CUSTOM interactions. Related tools: canvas table (drawing apps are pointer events), event listeners table (the subscription model beneath), and overscroll table (the gesture-adjacent fix).

Pointer Events UNIFY mouse, touch and pen into one event model: pointerdown/move/up with e.pointerType telling you which input is speaking - write the interaction once, every input works.

Bottom line: the two power-ups are setPointerCapture (drags continue outside the element - the slider-past-its-edge fix) and e.pointerId (track each finger separately for multi-touch). The gate: the browser swallows pointer moves for scrolling unless touch-action: none opts the element out.

The honest part: simple taps still fire click - the compatibility layer means plain buttons need NO pointer events. Pointer events are for CUSTOM interactions: drawing, dragging, gesture recognition, anything the click event cannot express.

How to use

  1. Write once, every input: el.addEventListener('pointerdown', startDrag) - no separate touchstart/mousedown pairs, no touch-action guessing.
  2. Capture the drag: el.setPointerCapture(e.pointerId) in pointerdown - pointermove keeps firing even when the pointer leaves the element, until pointerup.
  3. Clean up on pointercancel: the browser steals the pointer for scrolling or gestures - treat it as pointerup and reset state, never assume pointerup arrives.

Frequently asked questions

Why do pointer events exist when mouse and touch events already worked?

Three inputs, one model. Before pointers, interactive code wrote everything twice: mousedown/mousemove for desktop and touchstart/touchmove for mobile - with subtly different event shapes, coordinate properties and multi-touch handling. Pointer Events replace both: every input produces pointerdown/pointermove/pointerup, with e.pointerType ('mouse', 'touch', 'pen') when the input TYPE matters (pen pressure, hover for mouse). The migration rule: replace mouse* with pointer* and add touch-action CSS where the browser must not swallow moves - one codebase, every device, including future input types.

What does setPointerCapture actually solve?

The fleeing-pointer problem. During a drag, the pointer moves FAST - a slider thumb's pointermove events land on elements OUTSIDE the thumb once the finger outruns it, and the drag stutters or breaks. setPointerCapture(e.pointerId) redirects ALL subsequent events for that pointer to the capturing element until pointerup - the drag continues smoothly anywhere on the page, even outside the browser window. It is the fix for every slider, splitter, resize handle and drawing surface. Release is automatic on pointerup; pointercancel releases it too, which is why the cancel handler resets state.

What is pointercancel and why does it matter?

The system taking the pointer away. When the browser decides a gesture belongs to IT - the user started scrolling, the touch triggered a browser edge-swipe - it fires pointercancel and your pointerstream ENDS without a pointerup. Code assuming pointerup always arrives keeps phantom drag state: an element stuck following a pointer that is gone. The discipline: handle pointercancel exactly like pointerup (reset drag state, release capture) - and prevent the common cause with touch-action: none when your interaction legitimately owns the gesture. fighting pointercancel instead of handling it is the classic custom-gesture bug.

When should I still use click instead of pointer events?

For activation without custom gesture logic. The compatibility layer means a tap still fires click - so buttons, links and simple toggles work unchanged across all inputs with the plain click event, which also carries keyboard activation (Enter/Space) for free. Pointer events earn their complexity only for CONTINUOUS interactions: drawing strokes, dragging, pinch gestures, hover-dependent tooling, or anything needing pressure/tilt from a pen. The accessibility note follows from click's keyboard semantics: a pointerdown-driven action skips keyboard activation entirely unless you re-add it - which is why 'just use click' remains correct for everything that is fundamentally an activation.

Related tools