Skip to content

PointerSensor

PointerSensor listens to pointer events, touch events and mouse events and normalizes them into unified drag events.

Example

ts
import { PointerSensor } from 'dragdoll/sensors/pointer';
import { Draggable } from 'dragdoll/draggable';

// Create a pointer sensor instance which tracks pointer/touch/mouse events in
// window and emits drag events.
const pointerSensor = new PointerSensor(window);

// Listen to events.
pointerSensor.on('start', (e) => console.log('drag started', e));
pointerSensor.on('move', (e) => console.log('drag move', e));
pointerSensor.on('end', (e) => console.log('drag ended', e));
pointerSensor.on('cancel', (e) => console.log('drag canceled', e));

// Use the sensor to move an element.
const dragElement = document.querySelector('.dragElement');
const draggable = new Draggable([pointerSensor], {
  elements: () => [dragElement],
});

Class

ts
class PointerSensor<E extends PointerSensorEvents = PointerSensorEvents> implements Sensor<E> {
  constructor(element: Element | Window, options: Partial<PointerSensorSettings> = {}) {}
}

The PointerSensor class is a generic that implements the Sensor interface.

Type Variables

  1. E

Constructor Parameters

  1. element
    • The element (or window) whose events will be tracked.
  2. options
    • An optional PointerSensorSettings object, which you can also change later via updateSettings method.
    • You only need to provide the options you want to change, the rest will be left as default.
    • Default: {}.

Settings

The settings object follows the PointerSensorSettings interface. You can pass the settings object to the constructor or update them later via updateSettings method.

listenerOptions

ts
type listenerOptions = {
  capture?: boolean;
  passive?: boolean;
};

This object will be propagated to the source event listeners' options. You can use it to define whether the source event listeners should be passive and/or use capture.

Default: { capture: true, passive: true }

sourceEvents

ts
type sourceEvents = 'pointer' | 'touch' | 'mouse' | 'auto';

Define which type of events will be listened to and used as source events:

Default: "auto"

startPredicate

ts
type startPredicate = (e: PointerEvent | TouchEvent | MouseEvent) => boolean;

This function is called when drag process is starting up with the initial event as the argument. You can use it to define whether the drag process should be allowed to start (return true) or not (return false).

Default: (e) => ('button' in e && e.button > 0 ? false : true)

cancelOnVisibilityChange

ts
type cancelOnVisibilityChange = boolean;

Cancel the drag when the document becomes hidden (e.g. user switches tabs).

Default: true

cancelOnEscape

ts
type cancelOnEscape = boolean;

Cancel the drag when the Escape key is pressed.

Default: true

preventNativeDrag

ts
type preventNativeDrag = boolean;

Prevent the browser's native HTML5 drag behavior (e.g. for images and links) by calling preventDefault() on the window's dragstart event when a pointer interaction starts. This lets you avoid setting draggable="false" on individual elements.

Default: true

preventContextMenu

ts
type preventContextMenu = boolean;

Prevent the browser's context menu (right-click menu) from appearing during an active drag by calling preventDefault() on the window's contextmenu event. Useful to avoid jarring UX when the user right-clicks while dragging.

Default: false

Properties

element

ts
type element = Element | Window;

The observed element or window. Read-only.

drag

ts
type drag = PointerSensorDragData | null;

Current drag data or null when drag is inactive. The drag data follows the PointerSensorDragData interface. Read-only.

deltaX/deltaY represent the difference between the previous and current position, not the start and current position.

isDestroyed

ts
type isDestroyed = boolean;

Is sensor destroyed or not? Read-only.

Methods

on

ts
type on<T extends keyof E> = (
  type: T,
  listener: (e: E[T]) => void,
  listenerId?: SensorEventListenerId,
) => SensorEventListenerId;

Adds a listener to a sensor event. Returns a listener id, which can be used to remove this specific listener. By default this will always be a symbol unless manually provided.

Please check the Events section for more information about the events and their payloads.

Example

ts
pointerSensor.on('start', (e) => {
  console.log('start', e);
});

off

ts
type off<T extends keyof E> = (type: T, listenerId: SensorEventListenerId) => void;

Removes a listener (based on listener id) from a sensor event.

Example

ts
const id = pointerSensor.on('start', (e) => console.log('start', e));
pointerSensor.off('start', id);

updateSettings

ts
type updateSettings = (options: Partial<PointerSensorSettings>) => void;

Updates the sensor's settings. Accepts a partial PointerSensorSettings object as the first argument, only the options you provide will be updated. Read more about the settings in the Settings section.

Example

ts
pointerSensor.updateSettings({
  startPredicate: () => Math.random() > 0.5,
});

cancel

ts
type cancel = () => void;

Forcibly cancel the sensor's current drag process. The purpose of this method is to have a manual way of aborting the drag procedure at all times.

Example

ts
pointerSensor.cancel();

preventClickOnEnd

ts
type preventClickOnEnd = () => void;

Prevents the next click event from propagating and performing its default action. This is useful for blocking clicks after a drag ends to avoid triggering click handlers on draggable elements (e.g., links, buttons).

The blocker automatically removes itself after blocking a click or when a new pointer interaction starts on this sensor.

Features:

  • Only blocks native browser-generated clicks (e.isTrusted). Programmatic clicks (e.g., element.click()) are not blocked.
  • Uses both preventDefault() and stopPropagation() in the capture phase.
  • Self-cleaning: no arbitrary timeouts needed.

NOTE

This method is typically called automatically by Draggable when the preventClickOnEnd setting is enabled (which is the default). You usually don't need to call this manually unless you're using PointerSensor without Draggable.

Example

ts
// Manual usage (rare - usually handled by Draggable)
pointerSensor.on('end', () => {
  pointerSensor.preventClickOnEnd();
});

destroy

ts
type destroy = () => void;

Destroy the sensor. Disposes all allocated memory and removes all bound event listeners.

Example

ts
pointerSensor.destroy();

Events

NOTE

To minimize memory allocations, event data objects are pooled and mutated between events. Treat them as read-only and do not store them for later use. If you need to persist the data, extract the specific values you need or clone the object.

start

Emitted when the sensor starts dragging.

Payload follows the PointerSensorStartEvent interface.

move

Emitted when the sensor is moved during the drag.

Payload follows the PointerSensorMoveEvent interface.

cancel

Emitted when the drag is canceled.

Payload follows the PointerSensorCancelEvent interface.

end

Emitted when the drag ends without being canceled.

Payload follows the PointerSensorEndEvent interface.

destroy

Emitted when the sensor is destroyed.

Payload follows the PointerSensorDestroyEvent interface.

Exports

Here's a list of additional exports that are available in the dragdoll/sensors/pointer package.

PointerSensorDefaultSettings

ts
// Import
import { PointerSensorDefaultSettings } from 'dragdoll/sensors/pointer';

// Constant
const PointerSensorDefaultSettings: PointerSensorSettings;

Types

PointerSensorDragData

ts
// Import
import type { PointerSensorDragData } from 'dragdoll/sensors/pointer';

// Type
type PointerSensorDragData = {
  readonly pointerId: number;
  readonly pointerType: 'mouse' | 'pen' | 'touch';
  readonly startX: number;
  readonly startY: number;
  readonly x: number;
  readonly y: number;
  readonly deltaX: number;
  readonly deltaY: number;
};

PointerSensorSettings

ts
// Import
import type { PointerSensorSettings } from 'dragdoll/sensors/pointer';

// Interface
interface PointerSensorSettings {
  listenerOptions: { capture?: boolean; passive?: boolean };
  sourceEvents: 'pointer' | 'touch' | 'mouse' | 'auto';
  startPredicate: (e: PointerEvent | TouchEvent | MouseEvent) => boolean;
  cancelOnVisibilityChange?: boolean;
  cancelOnEscape?: boolean;
  preventNativeDrag?: boolean;
  preventContextMenu?: boolean;
}

PointerSensorStartEvent

ts
// Import
import type { PointerSensorStartEvent } from 'dragdoll/sensors/pointer';

// Interface
interface PointerSensorStartEvent {
  type: 'start';
  startX: number;
  startY: number;
  x: number;
  y: number;
  deltaX: number;
  deltaY: number;
  pointerId: number;
  pointerType: 'mouse' | 'pen' | 'touch';
  srcEvent: PointerEvent | TouchEvent | MouseEvent;
  target: EventTarget | null;
}

PointerSensorMoveEvent

ts
// Import
import type { PointerSensorMoveEvent } from 'dragdoll/sensors/pointer';

// Interface
interface PointerSensorMoveEvent {
  type: 'move';
  startX: number;
  startY: number;
  x: number;
  y: number;
  deltaX: number;
  deltaY: number;
  pointerId: number;
  pointerType: 'mouse' | 'pen' | 'touch';
  srcEvent: PointerEvent | TouchEvent | MouseEvent;
  target: EventTarget | null;
}

PointerSensorCancelEvent

ts
// Import
import type { PointerSensorCancelEvent } from 'dragdoll/sensors/pointer';

// Interface
interface PointerSensorCancelEvent {
  type: 'cancel';
  startX: number;
  startY: number;
  x: number;
  y: number;
  deltaX: number;
  deltaY: number;
  pointerId: number;
  pointerType: 'mouse' | 'pen' | 'touch';
  srcEvent: PointerEvent | TouchEvent | MouseEvent | null;
  target: EventTarget | null;
}

PointerSensorEndEvent

ts
// Import
import type { PointerSensorEndEvent } from 'dragdoll/sensors/pointer';

// Interface
interface PointerSensorEndEvent {
  type: 'end';
  startX: number;
  startY: number;
  x: number;
  y: number;
  deltaX: number;
  deltaY: number;
  pointerId: number;
  pointerType: 'mouse' | 'pen' | 'touch';
  srcEvent: PointerEvent | TouchEvent | MouseEvent | null;
  target: EventTarget | null;
}

PointerSensorDestroyEvent

ts
// Import
import type { PointerSensorDestroyEvent } from 'dragdoll/sensors/pointer';

// Interface
interface PointerSensorDestroyEvent {
  type: 'destroy';
}

PointerSensorEvents

ts
// Import
import type { PointerSensorEvents } from 'dragdoll/sensors/pointer';

// Interface
interface PointerSensorEvents {
  start: PointerSensorStartEvent;
  move: PointerSensorMoveEvent;
  cancel: PointerSensorCancelEvent;
  end: PointerSensorEndEvent;
  destroy: PointerSensorDestroyEvent;
}

DragDoll is released under the MIT License.