v148 · Web APIs · Event Sequence Viewer

Event Sequence Viewer

The expected vs actual event sequences for a drag gesture — before and after Chrome 148. The key change is that pointercancel now fires when the drag starts, correctly terminating the pointer event stream.

Both Pointer Events and the HTML Drag and Drop API can fire on the same element. Chrome 148 aligns the interaction: when drag starts, pointer events are cancelled via pointercancel and the drag event stream takes over.

event sequence comparison

Pointer Event
Drag Event
Cancel (new in 148)

Before Chrome 148

  • 1. pointerdown
  • 2. pointermove
  • 3. pointermove
  • 4. dragstart
  • 5. pointermove bug: still fires!
  • 6. drag
  • 7. pointermove bug: keeps firing
  • 8. drag
  • pointermove + drag interleaved
  • N. dragend
  • N+1. pointerup unexpected

Chrome 148+ (spec-compliant)

  • 1. pointerdown
  • 2. pointermove
  • 3. pointermove
  • 4. dragstart
  • 5. pointercancel new! 148+
  • 6. drag
  • 7. drag
  • drag events only (pointer stream ended)
  • N. drop / dragend

implications

Handling pointercancel on dragstart

In Chrome 148+, pointercancel fires on dragstart. Use this event to clean up any pointer-tracking state — release pointer capture, clear active tracking arrays, hide any pointer-position UI.

Custom DnD implementations

Implementations that listen to both pointer events and drag events should listen for pointercancel and stop pointer-based dragging when it fires. Don't rely on pointerup to end a drag.

touch-action: none

On touch devices, touch-action: none prevents the browser from intercepting touch events for scrolling. Combined with pointer events for drag, pointercancel fires if the browser takes over (e.g. for scrolling) — same mechanism.

No action needed for most apps

If your app uses the HTML Drag and Drop API exclusively (not pointer events), this change has no visible effect. It matters when mixing both APIs on the same draggable element.

code

const el = document.querySelector('[draggable="true"]');

el.addEventListener('pointercancel', e => {
  // Chrome 148+: fires when drag takes over the pointer stream
  // Clean up any pointer-tracking state:
  activePointers.delete(e.pointerId);
  stopCustomDragTracking();
});

el.addEventListener('dragstart', e => {
  // Drag API takes over here
  e.dataTransfer.setData('text/plain', 'dragged content');
});

// Pattern: use pointercancel (not pointerup) to end drag tracking
el.addEventListener('pointermove', e => {
  if (!activePointers.has(e.pointerId)) return;
  // Chrome 148: this stops firing after dragstart
  updateDragPosition(e);
});

see also

implementation reference

Need the exact API surface, compatibility boundaries, errors, lifecycle, and source links? Read the matching gendn reference ↗