Drag and Drop Elements with interact.js

Jess Mitchell

Drag and drop functionality is a common feature seen in apps to make the UI feel more interactive for users. There are several JavaScript libraries for achieving this effect, as well as the native HTML Drag and Drop API, but it can be difficult to pick what to use.

Here we’ll look at one of those libraries: interact.js, a customizable JavaScript library that can be used in HTML or front-end frameworks like React.

Setting Up Our HTML

Before we can make anything interactive, we need to set up our HTML elements. For the purpose of keeping things simple, let’s create a few items that will be draggable and one non-draggable dropzone to put the items in.

<section>
  <div class='item'>I'm an item</div>
  <div class='item'>I'm an item</div>
  <div class='item'>I'm an item</div>

  <div class="dropzone">
    <h3>Dropzone</h3>
  </div>
</section>
I'm an item
I'm an item
I'm an item

Dropzone

We’ve given each of our items a matching class (.item), which we’ll need later to make our items “interactable”. Nothing can be interacted with yet, though, so let’s move on the the JavaScript.

First, we’ll need to import interact.js. At the top of the HTML file you’re working in, add a script tag with the interact.js CDN link.

<script src="https://cdn.jsdelivr.net/npm/interactjs/dist/interact.min.js"></script>

If this were a React project, you could install via npm and import interact.js to your component. To do that, first run this in the command line:

$ npm install --save interactjs

Then import interact.js like so:

import interact from 'interactjs'

Now that we have our HTML elements and have imported interactjs, we can start actually using it! 🚀


Making Our Items “Interactable”

Since our items all have the class .item applied to them, we can select them all by using that class.

To make sure no other browser events related to the mouse happen while we’re dragging, we’ll first disable the browser panning and text selection on mouse drag.

.item {
  touch-action: none;
  user-select: none;
}

Now to move on to our JavaScript. We want to first make all our items interact.js items like so:

interact('.item')

Once our items are “interactable”, we can customize them as much as we want by chaining interact.js methods.

Note that just making our items "interactable" does not mean the items can be dragged yet. We need to explicitly declare dragging ourselves.

Dragging Items

Using the “interactable” item declared above, let’s now add our dragging options. To set custom dragging options, we can access the draggable method and pass it an object with all our custom settings. We will need to set at least one property in the object we pass draggable: the onmove property. This will allow us to update the x- and y-coordinates of our item as we drag it.

In onmove, we will:

  • Determine the initial x- and y-coordinates of our item with the data-x and data-y data attributes. These won’t exist until we create them after the first run through so we’ll use zero as a base value. We’ll also have to convert them to numbers from strings with parseFloat().
  • Get the delta (or difference) between the initial coordinates and where the mouse is now with the dx and dy values.
  • Add the initial values to the distance it has moved to get the new position for x and y.
  • Set those new positions with CSS’s transform property.
  • Update the data-x and data-y data attributes to this new position.
interact('.item')
  .draggable({
    onmove: function(event) {
      const target = event.target;

      const dataX = target.getAttribute('data-x');
      const dataY = target.getAttribute('data-y');
      const initialX = parseFloat(dataX) || 0;
      const initialY = parseFloat(dataY) || 0;

      const deltaX = event.dx;
      const deltaY = event.dy;

      const newX = initialX + deltaX;
      const newY = initialY + deltaY;

      target
        .style
        .transform = `translate(${newX}px, ${newY}px)`;

      target.setAttribute('data-x', newX);
      target.setAttribute('data-y', newY);
    }
  })

Since this function is getting pretty long, we can declare it outside the draggable property as a named function, and pass the named function instead. Once we start adding more properties, this we’ll keep our “interactable” item more readable. Our function can also be refactored (a lot) but, for our purposes, let’s keep it long so it’s clearer what we’re doing on each line.

function onMove (event) {
  const event = target.event;

  const dataX = target.getAttribute('data-x');
  const dataY = target.getAttribute('data-y');
  const initialX = parseFloat(dataX) || 0;
  const initialY = parseFloat(dataY) || 0;

  const deltaX = event.dx;
  const deltaY = event.dy;

  const newX = initialX + deltaX;
  const newY = initialY + deltaY;

  target
    .style
    .transform = `translate(${newX}px, ${newY}px)`;

  target.setAttribute('data-x', x);
  target.setAttribute('data-y', y);
}

items
  .draggable({
    onmove: onMove(event)
  })

Now that we’ve defined onmove, we officially have draggable items! They can be placed anywhere on the page, including the dropzone. The dropzone isn’t exactly meaningful yet, but we’ll get to that!

Demo of interact.js dragging

Note that all our items automatically have a new cursor too so it's clear which elements are "interactable". ✨

Inertia Throwing

Inertia throwing is a quick little add-on for dragging elements that gives the them a more natural feel on move. When enabled, it factors in the velocity at which the items are moved. When dragging stops, the item will continue to move a bit relative to the velocity at which it was being dragged.

To enable inertia throwing, update your draggable property like so:

items
  .draggable({
    onmove: onMove(event),
    inertia: true,
    restrict: {
      restriction: 'parent',
    },
  })

The restrict property will make sure you can’t “throw” your items off the screen. 🤓

Demo of interact.js inertia throwing

Creating a Dropzone

To create a dropzone, we need to create a new “interactable” element. Like we did with the .item class, we can select our dropzone element with the .dropzone class we set in our HTML.

interact('.dropzone')

With our .dropzone element selected, we can set which elements can be accepted in the dropzone and how much overlap they need to have to count as “dropped”. (0.75 means 75% of the element must overlap in this case.) We can set these through the dropzone method.

interact('.dropzone')
  .dropzone({
    accept: '.item',
    overlap: 0.75,
  })

Now, let’s decide on our goals.🐊 To make sure the dropzone is getting recognized, we can update the background color of our item in different states. interact.js thankfully has properties available for multiple states of dragging and dropping. We can update the color by adding classes to the item when each condition becomes true and remove those classes when they stop being true.

Let’s look at the interact.js properties, what they do, and the class we’ll use to represent each state:

  • ondropactivate: The item is getting dragged. Use the class .dragging
  • ondropdeactivate: The item is no longer being dragged. Remove the class .dragging
  • ondragenter: The item is considered to be in the dropzone. Add the class .can-drop
  • ondragleave: The item is getting moved out of the dropzone. Add the class .cannot-drop
interact('.dropzone')
  .dropzone({
    accept: '.item',
    overlap: 0.75,
    ondropactivate: function (event) {
      const item = event.relatedTarget
      item.classList.add('dragging')
    },
    ondropdeactivate: function (event) {
      const item = event.relatedTarget
      item.classList.remove('dragging', 'cannot-drop')
    },
    ondragenter: function(event) {
      const item = event.relatedTarget
      item.classList.remove('cannot-drop')
      item.classList.add('can-drop')
    },
    ondragleave: function(event) {
      const item = event.relatedTarget
      item.classList.remove('can-drop')
      item.classList.add('cannot-drop')
    }
  })

In each case, we select the item and apply or remove classes depending on what’s being done.

event.relatedTarget selects the item being dragged. To update the dropzone, use event.target. 🤓

After these properties are added to your dropzone element, make sure to add the CSS styles for .item.dragging, etc. Once that’s done, you’ll have items that can be dragged and get updated when they’re in the dropzone. 🔥 Updating the background color is just one example of what you can do but the sky’s the limit! 🌈

Demo of interact.js drag and drop

Customizing Your Elements Even More

interact.js is incredibly customizable. 🥳 Since you have access to the dropzone and item elements, you can update the styling of both, change the inner text, and add additional features like snapping into place. You can even use it for resizing your elements and add touchscreen dragging and dropping.

Check out the docs to learn even more options!

Browser Support

interact.js is supported by all modern browsers and even IE9+. 🏋 You can use it in your vanilla JS or whatever frontend framework you’re using.

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#