JavaScript Pointer Events Table
| Piece | What it does | Field note |
|---|---|---|
pointerdown / move / up | One event model | Mouse, touch, pen UNIFIED - write once, every input works |
e.pointerType | mouse / touch / pen | Branch on input type without separate event systems |
setPointerCapture(el) | Lock the pointer | Drags continue OUTSIDE the element - slider fix |
e.pointerId | Multi-touch identity | Track each finger separately - maps and multi-drag |
pointercancel | System stole it | Browser took over (scroll) - clean up, don't fight |
touch-action: none | Let pointer events live | Without it, the browser swallows moves for scrolling |
click still fires | Compatibility layer | tap = click - simple taps need NO pointer events |
e.isPrimary | The first finger | Ignore non-primary for single-pointer logic |
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
- Write once, every input: el.addEventListener('pointerdown', startDrag) - no separate touchstart/mousedown pairs, no touch-action guessing.
- Capture the drag: el.setPointerCapture(e.pointerId) in pointerdown - pointermove keeps firing even when the pointer leaves the element, until pointerup.
- 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.