By the end of this lesson, you will be able to handle mouse, touch, and pen input with one set of JavaScript Pointer Events.
What it is
Pointer Events are a browser API that reports mouse, touch, and pen input as one event family. A click, tap, and stylus press all produce pointerdown and pointerup; movement produces pointermove. The event object includes pointerType and a unique pointerId, so code can distinguish devices and track multiple pointers.
Related terms: pointer capture keeps events flowing to one element even when the pointer leaves it; primary pointer is the first active pointer for a device type; pressure and tilt are optional stylus details.
Why it matters
- Reduces duplicate mouse and touch handlers.
- Supports multi-touch by tracking each
pointerId. - Works well for drawing, dragging, and canvas tools.
- Improves consistency across desktop, mobile, and stylus devices.
Syntax or steps
- Select an element.
- Listen for
pointerdown,pointermove, orpointerup. - Read coordinates from
clientX,clientY, or element-relativeoffsetXandoffsetY. - Use
pointerIdto track multiple pointers. - Call
setPointerCapturewhen dragging should continue outside the element.
Example
const canvas = document.getElementById("pad");
const ctx = canvas.getContext("2d");
let drawing = false;
let lastX = 0;
let lastY = 0;
canvas.addEventListener("pointerdown", (event) => {
drawing = true;
lastX = event.offsetX;
lastY = event.offsetY;
canvas.setPointerCapture(event.pointerId);
});
canvas.addEventListener("pointermove", (event) => {
if (!drawing) return;
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(event.offsetX, event.offsetY);
ctx.lineWidth = event.pressure ? event.pressure * 8 : 2;
ctx.stroke();
lastX = event.offsetX;
lastY = event.offsetY;
});
canvas.addEventListener("pointerup", () => {
drawing = false;
});
canvas.addEventListener("pointercancel", () => {
drawing = false;
});
canvas.addEventListener("lostpointercapture", () => {
drawing = false;
});
Assume a canvas with id pad and CSS touch-action: none. pointerdown starts drawing and captures the pointer. pointermove draws only while drawing is true. event.pressure can make stylus strokes thicker or thinner. pointerup, pointercancel, and lostpointercapture stop drawing.
Common mistakes
- Forgetting
pointercancelorlostpointercapture. A touch can be canceled by scrolling or a system gesture, so reset state. - Using raw
clientXandclientYwithout converting to element coordinates. For canvas, useoffsetXandoffsetY, or scale withgetBoundingClientRect(). - Not setting
touch-action: nonefor drag or drawing areas. Without it, the browser may scroll or zoom instead of sending pointer moves. - Assuming every pointer has pressure or only one pointer exists. Use
pointerIdfor multi-pointer state and provide a fallback forevent.pressure.
When to use it
Use Pointer Events when you need one handler for mouse, touch, and pen. Use Mouse Events only for mouse-only behavior, such as right-click menus. Use Touch Events when you need legacy support or very specific multi-touch data.
| API | Best for | Watch out for |
|---|---|---|
| Pointer Events | Unified dragging, drawing, and stylus input | Need touch-action and cleanup |
| Mouse Events | Desktop-only hover, click, context menu | Do not handle touch or pen consistently |
| Touch Events | Legacy mobile code or custom multi-touch | More code and less stylus detail |
Practice
Guided exercise: create a small box and log the pointer type when the user presses it.
const box = document.getElementById("box");
box.addEventListener("pointerdown", (event) => {
console.log(event.pointerType, event.pointerId);
});
Expected output: something like mouse 1, touch 2, or pen 3.
Challenge: make the box change color while a pointer is down and return to normal on pointerup or pointercancel. Hint: toggle a CSS class inside the handlers.
Quick check
Question: Which property lets you track several fingers or pointers separately?
Answer: event.pointerId.
Summary
Pointer Events give JavaScript one consistent way to respond to mouse, touch, and pen input. By using pointerId, pointerType, and pointer capture, you can build dragging, drawing, and multi-pointer interactions without separate handlers for each device.