events

events

Listen to drag start, drag, and drag end events

The events plugin provides callbacks that fire during different phases of dragging. Get notified when dragging starts, during movement, and when it ends - perfect for updating UI state, logging, or triggering animations.

events({
  onDragStart: (data) => console.log('Started dragging'),
  onDrag: (data) => console.log('Moving:', data.offset),
  onDragEnd: (data) => console.log('Finished dragging'),
});

Basic Usage

import { events, Draggable } from '@neodrag/vanilla';

const element = document.getElementById('element');
const statusEl = document.getElementById('status');
const positionEl = document.getElementById('position');

new Draggable(element, [
  events({
    onDragStart: () => {
      statusEl.textContent = 'dragging';
    },

    onDrag: (data) => {
      positionEl.textContent = `${data.offset.x}, ${data.offset.y}`;
    },

    onDragEnd: () => {
      statusEl.textContent = 'idle';
    },
  }),
]);

Event Data

All event callbacks receive a DragEventData object with:

  • offset - Current position { x: number, y: number }
  • rootNode - Element with draggable applied
  • currentNode - Element being dragged (can change mid-drag)
  • event - The pointer event that triggered this callback
import { events, Draggable } from '@neodrag/vanilla';

const element = document.getElementById('element');

function logEventData(data) {
  console.log('Position:', data.offset);
  console.log('Root element:', data.rootNode);
  console.log('Current element:', data.currentNode);
  console.log('Pointer event:', data.event);
}

new Draggable(element, [
  events({
    onDragStart: logEventData,
    onDrag: logEventData,
    onDragEnd: logEventData,
  }),
]);

Real-World Examples

Position Tracking with Velocity

import { events, Draggable } from '@neodrag/vanilla';

const element = document.getElementById('tracker');
const positionEl = document.getElementById('position');
const velocityEl = document.getElementById('velocity');

let lastTime = 0;
let lastPosition = { x: 0, y: 0 };

new Draggable(element, [
  events({
    onDrag: (data) => {
      const now = Date.now();
      const deltaTime = now - lastTime;

      let velocityX = 0,
        velocityY = 0;
      if (deltaTime > 0) {
        velocityX = Math.round(
          ((data.offset.x - lastPosition.x) / deltaTime) * 1000,
        );
        velocityY = Math.round(
          ((data.offset.y - lastPosition.y) / deltaTime) * 1000,
        );
      }

      positionEl.textContent = `(${data.offset.x}, ${data.offset.y})`;
      velocityEl.textContent = `(${velocityX}px/s, ${velocityY}px/s)`;

      lastPosition = { ...data.offset };
      lastTime = now;
    },
  }),
]);

Save Position to Storage

import { events, Draggable } from '@neodrag/vanilla';

const element = document.getElementById('element');

new Draggable(element, [
  events({
    onDragEnd: (data) => {
      localStorage.setItem(
        'elementPosition',
        JSON.stringify({
          x: data.offset.x,
          y: data.offset.y,
          timestamp: Date.now(),
        }),
      );
      console.log('Position saved!');
    },
  }),
]);

Analytics Tracking

// Track user interactions for analytics
events({
  onDragStart: (data) => {
    analytics.track('drag_started', {
      element: data.rootNode.id,
      timestamp: Date.now(),
    });
  },

  onDragEnd: (data) => {
    analytics.track('drag_completed', {
      element: data.rootNode.id,
      finalPosition: data.offset,
      timestamp: Date.now(),
    });
  },
});

Progress Indicator

// Show progress based on how far element has moved
const maxDistance = 300;

events({
  onDragStart: () => {
    progressBar.style.display = 'block';
  },

  onDrag: (data) => {
    const distance = Math.sqrt(
      data.offset.x ** 2 + data.offset.y ** 2,
    );
    const progress = Math.min((distance / maxDistance) * 100, 100);
    progressBar.style.width = `${progress}%`;
  },

  onDragEnd: () => {
    progressBar.style.display = 'none';
  },
});

How It Works

The events plugin:

  1. Stores event data in its state during each hook
  2. Schedules callbacks using ctx.effect.immediate() for fast execution
  3. Provides consistent data across all three event types
  4. Non-cancelable - Always runs regardless of other plugin cancellations

The callbacks fire in this order:

  • onDragStartonDrag (repeatedly) → onDragEnd

API Reference

function events(options?: {
  onDragStart?: (data: DragEventData) => void;
  onDrag?: (data: DragEventData) => void;
  onDragEnd?: (data: DragEventData) => void;
}): Plugin;

Options:

  • onDragStart - Called when dragging begins
  • onDrag - Called repeatedly during dragging
  • onDragEnd - Called when dragging ends

DragEventData:

  • offset - Current position { x: number, y: number }
  • rootNode - Element with draggable applied
  • currentNode - Element being dragged
  • event - The pointer event that triggered this callback

Returns: A plugin object for use with draggable.