@svelte-put/movable GitHub

move node on mousedown

@svelte-put/movable @svelte-put/movable @svelte-put/movable @svelte-put/movable changelog

Migrating to Svelte 5? See the new docs site here.

Installation

npm install --save-dev @svelte-put/movable
pnpm add -D @svelte-put/movable
yarn add -D @svelte-put/movable

Quick Start

In the following minimal example, try dragging the blue box around.

Example

Box below can be moved around

...
<script lang="ts">
  import { movable } from '@svelte-put/movable';
</script>

<div class="grid h-20 w-20 place-items-center bg-blue-200 text-black" use:movable>...</div>

Events

<div use:movable on:movablestart on:movableend />

movable provides two CustomEvents, movablestart and movableend, with event.detail as MovableEventDetail.

export interface MovableEventDetail {
	/** the node that the action was placed on */
	node: HTMLElement;
	/** last known position, as in styles.position */
	position: {
		/** styles.position.left */
		left: number;
		/** styles.position.right */
		top: number;
	};
}

Setting Limit

By default, as seen in Quick Start, node that uses movable can be moved freely. The limit option can be set to limit the “movable” zone.

Limit within Screen

<div use:movable={{ limit: { parent: 'screen' } }} />

Limit to the viewport by setting limit.parent to 'screen'.

Example

Box below can be moved around, but only within viewport

...
<script>
  import { movable } from '@svelte-put/movable';
</script>

<div
  class="grid h-20 w-20 place-items-center bg-blue-200 text-black"
  use:movable={{ limit: { parent: 'screen' } }}
>
  ...
</div>

Limit within an HTMLElement Ancestor

<div use:movable={{ limit: { parent: ancestorNode } }} />

Limit to an ancestor by referencing limit.parent to that ancestor.

Box below can be moved around, but only within the violet border

...
<script lang="ts">
  import { movable } from '@svelte-put/movable';

  let parentNode: HTMLElement;
</script>

<div class="grid place-items-center border-2 border-violet-500 p-4" bind:this={parentNode}>
  <p class="text-center">Box below can be moved around, but only within the violet border</p>
  <div
    class="grid h-20 w-20 place-items-center bg-blue-200 text-black"
    use:movable={{ limit: { parent: parentNode } }}
  >
    ...
  </div>
</div>

movable cannot take into account scrollbars. So, for example, if your limit.parent is a scroll container with visible vertical scrollbar, moving the “movable” node to the scrollbar region will cause overflow in the horizontal direction, triggering the horizontal scrollbar to appear.

Limit within Delta

<div use:movable={{ limit: { delta: '50%' } }} />
<div use:movable={{ limit: { delta: '250px' } }} />
<div use:movable={{ limit: { delta: { x: '20%', y: '100px' } } }} />

Set limit.delta to limit movable to a certain travel distance. It takes a number with unit of % or px in one or both axes, and can be set insolation or in combination with limit.parent.

Example

Box below can be moved around, but only within a delta of ±100% (of its own size).

...
<script>
  import { movable } from '@svelte-put/movable';
</script>

<div
  class="grid h-20 w-20 place-items-center bg-blue-200 dark:text-black"
  use:movable={{ limit: { delta: '100%' } }}
>
  ...
</div>

Limit to Single Axis

<div use:movable={{ limit: { delta: { x: 0 } } }} />
<div use:movable={{ limit: { delta: { y: 0 } } }} />

Setting limit.delta.x or limit.delta.y to 0 effectively disables movement in that axis.

Example

Box below can be moved around only in the x axis, combined with a delta limit of ±100% (of its width).

...
<script>
  import { movable } from '@svelte-put/movable';
</script>

<div
  class="grid h-20 w-20 place-items-center bg-blue-200 dark:text-black"
  use:movable={{ limit: { delta: { x: '100%', y: 0 } } }}
>
  ...
</div>

Custom Handle

Handle is an HTMLElement on which movable will listen for pointer events to initiate and map movement to the target node.

<div use:movable={{ handle: someNode }} />

By default, the target node itself is the handle. To use a custom handle, set handle to the desired HTMLElement.

Example

Move blue box by dragging the blue box

.
...
<script lang="ts">
  import { movable } from '@svelte-put/movable';

  let handle: HTMLElement;
</script>

<div class="flex h-40 w-40 flex-col bg-blue-200 p-2 text-black" use:movable={{ handle }}>
  <div class="grid h-8 w-8 place-items-center self-end bg-green-200" bind:this={handle}>.</div>
  <div class="grid flex-1 place-items-center self-stretch">...</div>
</div>

Ignore Children Nodes from Triggering movable on Click

Prefer handle in favor of ignore when possible for better predictability.

The ignore option takes one or more CSS selector strings and will prevent matching children of handle from triggering movable.

Example

mousedown on red box will not trigger movable

<script>
  import { movable } from '@svelte-put/movable';
  import { onMount } from 'svelte';

  let mounted = false;
  onMount(() => {
    setTimeout(() => {
      mounted = true;
    }, 500);
  });
</script>

<div
  class="grid h-40 w-40 place-items-center bg-blue-200 dark:text-black"
  use:movable={{ ignore: '.to-ignore' }}
>
  {#if mounted}
    <p class="to-ignore cursor-auto bg-red-200 p-2 text-sm">To ignore</p>
  {/if}
</div>

Disabling Cursor Handling

<div use:movable={{ cursor: false }} />

By default, movable adds cursor: grab to handle, and cursor: grabbing on mousedown to both handle and window.body. This can be disabled by setting the cursor option to false.

Automatic cursor handling is done on action initialization and update. It won’t work for ignored children that are dynamically added to DOM at runtime. In such cases, disable cursor or add cursor styles manually to the ignored children.


Happy moving node! 👨‍💻

Edit this page on Github