NAV Navbar

Welcome

interact.js is a JavaScript module for drag and drop, resizing and multi-touch gestures with inertia and snapping for modern browsers (and also IE9+).

To get a quick overview of how interact.js is intended to be used, read this post on Mozilla Hacks.

For a reference of all methods, go to the API Reference.

There’s a guide for migrating from v1.2 to v1.3

Installation

npm dependency

$ npm install --save interactjs
const interact = require('interactjs');

Install the package as a dependency with npm then require the interact function.

CDN

<script src="https://cdn.jsdelivr.net/npm/interactjs@1.3.0/dist/interact.min.js"></script>
<!-- or -->
<script src="https://unpkg.com/interactjs@1.3.0/dist/interact.min.js"></script>

You can use the jsDelivr or unpkg CDNs, simply add to your HTML a script tag pointing to their servers.

Module

interact is exposed as a CommonJS module, an AMD module, or a global object depending on what the environment supports.

Interactables

interact('.drag-and-resize')
  .draggable({
    snap: {
      targets: [
        { x: 100, y: 200 },
        function (x, y) { return { x: x % 20, y: y }; }
    ]}
  })
  .resizable({
    inertia: true
  });

The interact function takes an Element or CSS selector and an optional options object. This returns an Interactable object which has several methods and properties to configure what events it can fire and to modify the reported coordinates. These methods have a fluent interface so method calls can be chained nicely.

Selector Contexts

<ul id="my-list">
  <li> item 1 </li>
  <li> item 2 </li>
  <li> item 3 </li>
</ul>
var myList = document.querySelector('#my-list');

interact('li', { context: myList }) .draggable({ /* ... */ });

If you want to use a selector to target elements but only want to target the children of a certain element, add a context element to the options object when creating the Interactable.

interact('li') === interact('li', { context: document })  // true
interact('li') === interact('li', { context: myList })    // false

interact returns a different object if the same selector is used but a different context is specified. The default context is the document.

Drag, resize and Gesture handles

<div id="movable-box">
  <div id="drag-point" />
</div>
var movable = document.querySelector('#movable-box');

interact(movable) .draggable({ allowFrom: '.drag-handle', onmove: function (event) { /* ... / } }) .resizable({ allowFrom: '.resize-handle', onmove: function (event) { / ... / } }) .pointerEvents({ allowFrom: '', });;

The allowFrom option lets you specify a target CSS selecctor or Element which must be the target of the pointer down event in order for the action to start. This option available for drag, resize and gesture, as well as pointerEvents (down, move, hold, etc.). Using the allowFrom option, you may specify handles for each action separately and for all your pointerEvents listeners.

Ignorable Selectors

<div id="movable-box">
  <div id="undraggable-when-on-this-element" />
</div>
var movable = document.querySelector('#movable-box');

interact(movable) .draggable({ ignoreFrom: '.content', onmove: function (event) { /* ... */ } }) .pointerEvents({ ignoreFrom: '[no-pointer-event]', });

Like allowFrom, ignoreFrom gives you the ability to avoid certain elements in your interactable element. Which is good when certain elements need to maintain default behavior when interacted with.

For example, dragging around a text/contentEditable, by wrapping this object with a draggable element and ignoring the editable content you maintain the ability to highlight text without moving the element.

Action options

interact(target)
  .draggable({
    max          : Number,
    maxPerElement: Number,
    manualStart  : Boolean,
    hold         : Number,
    snap         : {/* ... /},
    restrict     : {/ ... /},
    inertia      : {/ ... /},
    autoScroll   : {/ ... */},

axis : 'x' || 'y'

}) .resizable({ max : Number, maxPerElement: Number, manualStart : Boolean, hold : Number, snap : {/* ... /}, restrict : {/ ... /}, inertia : {/ ... /}, autoScroll : {/ ... */},

square : true || false, axis : 'x' || 'y' }) .gesturable({ max : Number, maxPerElement: Number, manualStart : Boolean, hold : Number, restrict : {/* ... */} });

The Interactable methods draggable, resizable and gesturable take either true or false to simply allow/disallow the action or an object with properties to change certain settings.

If an object parameter is given, the action is enabled unless enabled: false is a property of the object.

enabled

Enable the action for the Interactable. If the options object has no enabled property or the property value is true then the action is enabled. If enabled is false, the action is disabled.

max

max is used to limit the number of concurrent interactions that can target an interactable. By default, any number of interactions can target an interactable.

maxPerElement

By default only 1 interaction can target the same interactable+element combination. If you want to allow multiple interactions on the same target element, set the maxPerElement property of your object to a value >= 2.

manualStart

If this is changed to true then drag, resize and gesture actions will have to be started with a call to Interaction#start as the usual down, move, <action>start… sequence will not start an action.

hold

The action will start after the pointer is held down for the given number of milliseconds.

snap

Change snapping settings for drag and resize. See docs/snapping.

restrict

Change restriction settings for drag, resize and gesture. See docs/restriction.

inertia

Change inertia settings for drag, and resize. See docs/inertia.

autoScroll

interact(element)
  .draggable({
    autoScroll: true,
  });
  .resizable({
    autoScroll: {
      container: document.body,
      margin: 50,
      distance: 5,
      interval: 10
    }
  });

Scroll a container (window or an HTMLElement) when a drag or resize move happens at the edge of the container.

drag axis

interact(target).draggable({
  axis: 'x'
});

The axis in which the first movement must be in for the drag sequence to start. After the movement in that axis, the the action can move in both the x and y axes.

resize axis

interact(target).resizable({
  axis: 'x'
});

The axis in which resizing is allowed.

resize edges

interact(target).resizable({
  edges: {
    top   : true,       // Use pointer coords to check for resize.
    left  : false,      // Disable resizing from left edge.
    bottom: '.resize-s',// Resize if pointer target matches selector
    right : handleEl    // Resize if pointer target is the given Element
  }
});

If resize edges are used, resize events will have rect and deltaRect properties. In resizestart, rect will be identical to the rect returned by interactable.getRect(element) and deltaRect will have all-zero properties. rect is updated on each resizemove and the values in deltaRect reflect the changes.

<div class="resizable">
  <!-- top-left resize handle -->
  <div class="resize-top    resize-left"></div>

<!-- bottom-right resize handle --> <div class="resize-bottom resize-right"></div> </div>

interact('.resizable').resizable({
  edges: {
    top   : '.resize-top',
    left  : '.resize-left',
    bottom: '.resize-bottom',
    right : '.resize-right',
  },
});

If you’d like an element to behave as a resize corner, let it match the selectors of two adjacent edges.

Resize handle elements must be children of the resizable element. If you need the handles to be outside the target element, then you will need to use Interaction#start.

resize invert

interact(target).resizable({
  edges: { bottom: true, right: true },
  invert: 'reposition'
});

Choose what should happen if the target would be resized to dimensions less than 0x0. The possible values are:

Demo on Codepen

resize square

interact(target).resizable({
  squareResize: true
});

When resizing, change the width and height by the same amount. This doesn’t necessarily maintain the aspect ratio of the object.

dropzone accept

interact(target).dropzone({
  accept: '.drag0, .drag1',
});

The CSS selector or element which must match the dragged element in order for drop events to be fired.

dropzone overlap

interact(target).dropzone({
  overlap: 0.25
});

Set how drops are checked for. The allowed values are:

dropzone checker

interact(target).dropzone({
  checker: function (dragEvent,         // related dragmove or dragend
                     event,             // Touch, Pointer or Mouse Event
                     dropped,           // bool default checker result
                     dropzone,          // dropzone Interactable
                     dropElement,       // dropzone elemnt
                     draggable,         // draggable Interactable
                     draggableElement) {// draggable element

// only allow drops into empty dropzone elements return dropped && !dropElement.hasChildNodes(); } });

A dropzone checker is an optional function that can be used to add additional conditions to check if a dragged element can be dropped into a dropzone.

The checker function takes the following arguments:

Events

InteractEvents

function listener (event) {
  console.log(event.type, event.pageX, event.pageY);
}

interact(target) .on('dragstart', listener) .on('dragmove dragend', listener) .on(['resizemove', 'resizeend'], listener) .on({ gesturestart: listener, gestureend: listener });

interact(target).draggable({ onstart: listener, onmove: listener, onend: listener });

interact.on('dragmove resizestart', function (event) { console.log(event.type, event.pageX, event.pageY); });

The InteractEvent types are:

To respond to InteractEvents, you must add listeners for the event types either directly on an interactable or globally for all events of those types on the interact object.

The event object that was created is passed to these functions as the first parameter.

InteractEvent properties include the usual properties of mouse/touch events such as pageX/Y, clientX/Y, modifier keys etc. but also some properties providing information about the change in coordinates and event specific data. The table below displays all of these events.

Common
target The element that is being interacted with
interactable The Interactable that is being interacted with
interaction The Interaction that the event belongs to
x0, y0 Page x and y coordinates of the starting event
clientX0, clientY0 Client x and y coordinates of the starting event
dx, dy Change in coordinates of the mouse/touch
velocityX, velocityY The Velocity of the pointer
speed The speed of the pointer
timeStamp The time of creation of the event object
Drag
dragmove
dragEnter The dropzone this Interactable was dragged over
dragLeave The dropzone this Interactable was dragged out of
Resize
axes The axes the resizing is constrained to (x/y/xy)
Gesture
distance The distance between the event’s first two touches
angle The angle of the line made by the two touches
da The change in angle since previous event
scale The ratio of the distance of the start event to the distance of the current event
ds The change in scale since the previous event
box A box enclosing all touch points

In gesture events, page and client coordinates are the averages of touch coordinates. Velocity is calculated from these averages.

Drop Events

interact(dropTarget)
  .dropzone({
    ondrop: function (event) {
      alert(event.relatedTarget.id
            + ' was dropped into '
            + event.target.id);
    }
  })
  .on('dropactivate', function (event) {
    event.target.classList.add('drop-activated');
  });

Dropzones can receive the following events: dropactivate, dropdeactivate, dragenter, dragleave, dropmove, drop.

The dropzone events are plain objects with the following properties:

target The dropzone element
dropzone The dropzone Interactable
relatedTarget The element that’s being dragged
draggable The Interactable that’s being dragged
dragEvent The related drag event – drag{start,move,end}
timeStamp Time of the event
type The event type

Pointer Events

interact(target).on(pointerEventType, listenerFunction);

I call these pointerEvents because they present the events roughly as the real PointerEvent interface does, specifically:

Configuring pointer events

interact(target).pointerEvents({
  holdDuration: 1000,
  ignoreFrom: '[no-pointer]',
  allowFrom: '.handle',
  origin: 'self',
});

pointerEvent are not snapped or restricted, but can be modified with the origin modifications. tap events have a dt property which is the time between the related down and up events. For doubletap dt is the time between the two previous taps. dt for hold events is the length of time that the pointer has been held down for (around 600ms).

fast click

// fast click
interact('a[href]').on('tap', function (event) {
  window.location.href = event.currentTarget.href;

event.preventDefault(); });

tap and doubletap don’t have the delay that click events have on mobile devices so it works great for fast buttons and anchor links. Also, unlike regular click events, a tap isn’t fired if the pointer is moved before being released.