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

<script>
  import { events } from '@neodrag/svelte';

  let dragStatus = $state('idle');
  let position = $state({ x: 0, y: 0 });

  const eventHandlers = events({
    onDragStart: () => {
      dragStatus = 'dragging';
    },

    onDrag: (data) => {
      position = { x: data.offset.x, y: data.offset.y };
    },

    onDragEnd: () => {
      dragStatus = 'idle';
    },
  });
</script>

<div {@attach draggable([eventHandlers])}>
  Status: {dragStatus}
  <br />
  Position: {position.x}, {position.y}
</div>

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
<script>
  import { events } from '@neodrag/svelte';

  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);
  }

  const eventHandlers = events({
    onDragStart: logEventData,
    onDrag: logEventData,
    onDragEnd: logEventData,
  });
</script>

<div {@attach draggable([eventHandlers])}>
  Check console for event data
</div>

Real-World Examples

Position Tracking with Velocity

<script>
  import { events } from '@neodrag/svelte';

  let position = $state({ x: 0, y: 0 });
  let velocity = $state({ x: 0, y: 0 });
  let lastTime = 0;
  let lastPosition = { x: 0, y: 0 };

  const trackingHandler = events({
    onDrag: (data) => {
      const now = Date.now();
      const deltaTime = now - lastTime;

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

      position = { ...data.offset };
      lastPosition = { ...data.offset };
      lastTime = now;
    },
  });
</script>

<div {@attach draggable([trackingHandler])}>
  Position: ({position.x}, {position.y})
  <br />
  Velocity: ({velocity.x}px/s, {velocity.y}px/s)
</div>

Save Position to Storage

<script>
  import { events } from '@neodrag/svelte';

  const saveHandler = events({
    onDragEnd: (data) => {
      localStorage.setItem(
        'elementPosition',
        JSON.stringify({
          x: data.offset.x,
          y: data.offset.y,
          timestamp: Date.now(),
        }),
      );
      console.log('Position saved!');
    },
  });
</script>

<div {@attach draggable([saveHandler])}>
  Position saves when you stop dragging
</div>

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.