/draggable

The JavaScript Drag & Drop library your grandparents warned you about.

Primary LanguageJavaScriptMIT LicenseMIT

CircleCI status

Warning: Draggable is currently in beta

Get complete control over drag and drop behaviour with Draggable! Draggable abstracts native browser events into a comprehensive API to create a custom drag and drop experience. Draggable comes with additional modules: Sortable, Droppable, Swappable. Draggable itself does not perform any sorting behaviour while dragging, but does the heavy lifting, e.g. creates mirror, emits events, manages sensor events, makes elements draggable.

The additional modules are built on top of Draggable and therefore provide a similar API interface, for more information read the documentation below.

Features

  • Works with native drag, mouse, touch and force touch events
  • Can extend dragging behaviour by hooking into draggables event life cycle
  • Can extend drag detection by adding sensors to draggable
  • The library is targeted ES6 first

Table of Contents

Install

You can install the library via npm.

npm install @shopify/draggable --save

or via yarn:

yarn add @shopify/draggable

Draggable

API

new Draggable(containers: Array[HTMLElement]|NodeList, options: Object): Draggable
To create a draggable instance you need to specify the containers that hold draggable items, e.g. [document.body] would work too. The second argument is an options object, which is described below.

draggable.on(eventName: String, listener: Function): Draggable
Draggable is an event emitter, so you can register callbacks for events. Draggable also supports method chaining.

draggable.off(eventName: String, listener: Function): Draggable
You can unregister listeners by using .off(), make sure to provide the same callback.

draggable.trigger(eventName: String, event: AbstractEvent): Draggable
You can trigger events through draggable. This is used to fire events internally or by extensions of Draggable.

draggable.destroy(): void
Detaches all sensors and listeners, and cleans up after itself.

Options

draggable {String}
A css selector for draggable elements within the containers specified. By default it will look for an element with .draggable-source class. Default: .draggable-source

handle {String}
Specify a css selector for a handle element if you don't want to allow drag action on the entire element. Default: null

delay {Number}
If you want to delay a drag start you can specify delay in milliseconds. This can be useful for draggable elements within scrollable containers. Default: 0

native {Boolean}
If enabled Draggable will use the browsers native drag events to detect drag behaviour. By default it will use mouse events to detect drag behaviour. You can only customize the mirror element when using mouse events, otherwise mirror will be null in events. Default: false

plugins {Array[Plugin]}
Plugins add behaviour to Draggable by hooking into its life cycle, e.g. one of the default plugins controls the mirror movement. Default: [Mirror, Accessibility]

classes {Object}
Draggable adds classes to elements to indicate state. These classes can be used to add styling on elements in certain states.

Events

Name Description Cancelable Cancelable action
drag:start Gets fired when drag action begins true Prevents drag start
drag:move Gets fired when moving a draggable around false -
drag:over Gets fired when dragging over other draggable false -
drag:over:container Gets fired when dragging over other draggable container false -
drag:out Gets fired when dragging out of other draggable false -
drag:out:container Gets fired when dragging out of other draggable container false -
drag:stop Gets fired when draggable has been released false -
drag:pressure Gets fired when using force touch on draggable element false -
mirror:created Gets fired when draggable mirror gets created false -
mirror:attached Gets fired when draggable mirror gets attached to DOM false -
mirror:move Gets fired when draggable mirror moves true Stop mirror movement

Classes

Name Description Default
body:dragging Class added on the document body while dragging draggable--is-dragging
container:dragging Class added on the container where the draggable was picked up from draggable-container--is-dragging
source:dragging Class added on the draggable element that has been picked up draggable-source--is-dragging
source:placed Class added on the draggable element on drag:stop draggable-source--placed
container:placed Class added on the draggable container element on drag:stop draggable-container--placed
draggable:over Class added on draggable element you are dragging over draggable--over
container:over Class added on draggable container element you are dragging over draggable-container--over
mirror Class added on the mirror element draggable-mirror

Example

This sample code will make list items draggable:

import {Draggable} from '@shopify/draggable';

new Draggable(document.querySelectorAll('ul'))
  .on('drag:start', () => console.log('drag:start'))
  .on('drag:move',  () => console.log('drag:move'))
  .on('drag:stop',  () => console.log('drag:stop'));

Sortable

Sortable allows you to reorder elements. It maintains the order internally and fires three events on top of the draggable events: sortable:start, sortable:sorted and sortable:stop.

API

new Sortable(containers: Array[HTMLElement]|NodeList, options: Object): Sortable
To create a sortable instance you need to specify the containers that hold draggable items, e.g. [document.body] would work too. The second argument is an options object, which is described below.

sortable.on(eventName: String, listener: Function): Sortable
Sortable uses Draggables event emitter, so you can register callbacks for events. Sortable also supports method chaining.

sortable.off(eventName: String, listener: Function): Sortable
You can unregister listeners by using .off(), make sure to provide the same callback.

sortable.trigger(eventName: String, event: AbstractEvent): Sortable
You can trigger events through sortable. This is used to fire events internally or by extensions of Draggable.

sortable.destroy(): void
Detaches all sensors and listeners, and cleans up after itself.

Options

Same as Draggable

Events

Name Description Cancelable Cancelable action
sortable:start Gets fired when drag action begins true Prevents drag start
sortable:sorted Gets fired when the source gets sorted in the DOM false -
sortable:stop Gets fired when dragging over other draggable false -

Classes

Same as Draggable

Example

This sample code will make list items draggable:

import {Sortable} from '@shopify/draggable';

new Sortable(document.querySelectorAll('ul'))
  .on('sortable:start', () => console.log('sortable:start'))
  .on('sortable:sorted', () => console.log('sortable:sorted'))
  .on('sortable:stop', () => console.log('sortable:stop'));

Droppable

Droppable allows you to declare draggable and droppable elements via options.

Sortable allows you to reorder elements. It maintains the order internally and fires three events on top of the draggable events: sortable:start, sortable:sorted and sortable:stop.

API

new Droppable(containers: Array[HTMLElement]|NodeList, options: Object): Droppable
To create a droppable instance you need to specify the containers that hold draggable items, e.g. [document.body] would work too. The second argument is an options object, which is described below.

droppable.on(eventName: String, listener: Function): Droppable
Droppable uses Draggables event emitter, so you can register callbacks for events. Droppable also supports method chaining.

droppable.off(eventName: String, listener: Function): Droppable
You can unregister listeners by using .off(), make sure to provide the same callback.

droppable.trigger(eventName: String, event: AbstractEvent): Droppable
You can trigger events through droppable. This is used to fire events internally or by extensions of Draggable.

droppable.destroy(): void
Detaches all sensors and listeners, and cleans up after itself.

Options

droppable {String|Array[HTMLElement]|NodeList|Function}
A css selector string, an array of elements, a NodeList or a function returning elements for droppable elements within the containers specified.

Events

Name Description Cancelable Cancelable action
droppable:over Gets fired when dragging over droppable element true Prevents drop
droppable:out Gets fired when dragging out of a droppable element true Prevents release

Classes

Name Description Default
droppable:active Class added on droppables when drag starts draggable-droppable--active
droppable:occupied Class added on droppable element, when it contains a draggable draggable-droppable--occupied

Example

This sample code will make list items draggable and allows to drop them inside another element:

import {Droppable} from '@shopify/draggable';

const droppable = new Droppable(document.querySelectorAll('ul'), {
  draggable: 'li',
  droppable: '#dropzone',
});

droppable.on('droppable:over', () => console.log('sortable:start'))
droppable.on('droppable:out', () => console.log('sortable:sorted'));

Swappable

Swappable allows you to swap elements by dragging over them. No order will be maintained (unlike Sortable), so any draggable element that gets dragged over will be swapped with the source element.

API

new Swappable(containers: Array[HTMLElement]|NodeList, options: Object): Swappable
To create a swappable instance you need to specify the containers that hold draggable items, e.g. [document.body] would work too. The second argument is an options object, which is described below.

swappable.on(eventName: String, listener: Function): Swappable
Swappable uses Draggables event emitter, so you can register callbacks for events. Swappable also supports method chaining.

swappable.off(eventName: String, listener: Function): Swappable
You can unregister listeners by using .off(), make sure to provide the same callback.

swappable.trigger(eventName: String, event: AbstractEvent): Swappable
You can trigger events through swappable. This is used to fire events internally or by extensions of Draggable.

droppable.destroy(): void
Detaches all sensors and listeners, and cleans up after itself.

Options

Same as Draggable

Events

Name Description Cancelable Cancelable action
swappable:start Gets fired when starting to drag true Prevents drag
swappable:swapped Gets fired before the source gets swapped true Prevents swap
swappable:stop Gets fired when dragging out of a droppable element false -

Classes

Same as Draggable

Example

This sample code will make list items draggable and allows to drop them inside another element:

import {Swappable} from '@shopify/draggable';

const swappable = new Swappable(document.querySelectorAll('ul'), {
  draggable: 'li',
});

swappable.on('swappable:start', () => console.log('swappable:start'))
swappable.on('swappable:swapped', () => console.log('swappable:swapped'));
swappable.on('swappable:stop', () => console.log('swappable:stop'));

Plugins

Draggables ships with two optional plugins: Collidable & Snappable

Collidable

When you use the collidable plugin you can specify which elements you can't drag over and it will freeze the mirror movement for you. These currently only work with Sortable, Swappable and Droppable.

Options

collidables {String|Array[HTMLElement]|NodeList|HTMLElement|Function}
A css selector string, an array of elements, a NodeList, a HTMLElement or a function returning elements for collidable elements.

Events

Name Description Cancelable Cancelable action
collidable:in Gets fired when dragging near a collidable element false -
collidable:out Gets fired when dragging away from a collidable element false -

Example

import {Sortable, Collidable} from '@shopify/draggable';

const sortable = new Sortable(document.querySelectorAll('ul'), {
  draggable: 'li',
  collidables: '.other-list',
  plugins: [Collidable]
});

sortable.on('collidable:in', () => console.log('collidable:in'));
sortable.on('collidable:out', () => console.log('collidable:out'));

Snappable

Snappable simulates a "snap" by hiding the mirror and removing the 'source:dragging' class from the source. It also sets the 'source:placed' class for potential drop animations.

Options

No options

Events

Name Description Cancelable Cancelable action
snap:in Gets fired when just before snapping in true Prevents snapping
snap:out Gets fired when snapping out false Prevents snapping out

Example

import {Sortable, Snappable} from '@shopify/draggable';

const sortable = new Sortable(document.querySelectorAll('ul'), {
  draggable: 'li',
  plugins: [Snappable]
});

sortable.on('snap:in', () => console.log('snap:in'));
sortable.on('snap:out', () => console.log('snap:out'));

Known issues

  • Touch events freeze sometimes while dragging
  • Safari 11 uses force touch, but there are no visual guidelines on the website

Contributing

Contributions are more than welcome, the code base is still new and needs more love.

  1. Clone the repo: git clone https://github.com/Shopify/draggable.git
  2. cd draggable
  3. yarn install or npm install
  4. Run tests with yarn test or npm run test
  5. Submit PR

Roadmap

The roadmap for the official release is still in the works. More to come

Copyright

Copyright (c) 2017 Shopify. See LICENSE.md for further details.