DEV Community

Cover image for Cross application drag 'n drop using Transmat
Jorik
Jorik

Posted on

Cross application drag 'n drop using Transmat

With Transmat you can add drag-drop and copy-paste interactions to your webpage. That is nothing new, but with its ability to interact with external applications by simply dragging (or copying!) elements, a new world of possibilities open up!

The Transmat library providing a wrapper around the browser's DataTransfer API, and makes it easy to respond to drag-drop interactions. This interaction is supported by all desktop browsers that have been released after Internet Explorer 11 (10 years ago!)

In this post, you'll learn how to transfer data to external applications, receive incoming external data, and how to highlight drop areas.

1. Transferable elements in your HTML

Add a draggable and focusable element to your webpage.

<div id="source" draggable="true" tabindex="0">
  Drag or copy me
</div>

<div id="target" tabindex="0">
  Drop here!
</div>
Enter fullscreen mode Exit fullscreen mode

The elements need to be marked as such in order to enable the browser's native Drag and Drop interactions. The tabindex will make the element selectable, which enables the user to trigger a copy.

2. Setting up drag and copy interactions

The examples below have the transmat library imported. You can get it from npm, npm install transmat.

import {Transmat, addListeners} from 'transmat';

const source = document.getElementById('source');

// Add listeners for 'drag' and 'copy'.
addListeners(source, 'transmit', event => {
  // Create a Transmat instance based on the incoming event.
  const transmat = new Transmat(event);
  transmat.setData({
    // Text data. This will show up in Text fields, 
    // but also as the fallback for text/html when pasting 
    // to WYSIWYG editors.
    'text/plain': 'Hi there!',

    // HTML data. This will be accepted by many WYSIWYG 
    // editors like Google Docs, Gmail, Microsoft Word, etc.
    'text/html': `
         <h1>This is formatted!</h1>
         <p>
           Pretty cool, and you can also add
           <a href="https://example.com/">links</a>!
         </p>
         <img src="https://example.com/test.jpg" />`,

    // URL data. This URL will open when dropping on the
    // URL bar of your browser, will create a shortcut file 
    // when dropping on the desktop.
    'text/uri-list': 'https://example.com/foobar',

    // Share a structured JS object. Transmat with serialize 
    // this object using JSON.stringify()
    'application/json': {name: 'Rory'},
  });
});
Enter fullscreen mode Exit fullscreen mode

Transmat works as following;

  • The addListeners method assigns the event transmit listeners for dragstart and copy events. The event callback gets invoked when one of these interactions get triggered.
  • A new Transmat(event) instance is created using the event object. This instance makes it easier for you to interact with the underlying DataTransfer API and ensures the same behavior across browsers.
  • Provide data with the setData(data) method. By providing MIME types, you will describe the type of data. The MIME types listed in the code example below are primitives that have wide support across the operating system. See the code comments to see the behavior for each of these.

Now if you'll run this example, see what happens when you drag-drop the example object to your favourite text editor (Codepen, Sublime, VS Code), WYSIWYG editor (Google Docs, Apple Pages, Microsoft Word) and browser windows. It will show and open the content you provided in this element. Like magic!

3. Listening for incoming data

The same way as listening for the transmit event, you can listen to incoming data using the receive event.

const target = document.getElementById('target');

// Add listeners for 'drop' and 'paste'.
addListeners(target, 'receive', event => {
  // Create a Transmat instance based on the incoming event.
  const transmat = new Transmat(event);
  if (
    // Only want to accept JSON data.
    transmat.hasType('application/json') && 
    // Call this in order to accept incoming data.
    transmat.accept()
  ) {
    // Get the JSON string data, parse it, and log.
    const jsonString = transmat.getData('application/json');
    const data = JSON.parse(jsonString);
    alert(`Hi ${data.name}!`);
  });
});
Enter fullscreen mode Exit fullscreen mode
  • Like as transmitting data, you will need to set up a receive event listener to listen for incoming data transfers.
  • A new Transmat(event) is created to interact with the underlying incoming data.
  • In this example, you only want to accept application/json data. The hasType(type) method will return whether this payload is being tranferred.
  • In order to accept the tranfer, you need to call the accept() method. This will prevent the document from doing its default behavior, like navigating to an URL when the text/uri-list payload is present.
  • When accepted, you will use the getData(type) to read the string payload. In this example, you expect JSON data which needs to be parsed first using JSON.parse.

4. Highlight drop areas

It can be hard for the user to discover valid drop areas. You can use the TransmatObserver class to observe incoming transfer events on targets, and respond to them. The example below will add a className when a JSON payload is being transferred.

<style>
.drag-active { background: rgba(0, 255, 0, .1); }
.drag-over { background: rgba(0, 255, 0, .5); }
</style>
Enter fullscreen mode Exit fullscreen mode
// Observe Transmat (drag) events happening on your page.
const obs = new TransmatObserver(entries => {
  for (const entry of entries) {
    const transmat = new Transmat(entry.event);
    // Only want to highlight elements containing JSON data.
    if(transmat.hasType('application/json')) {
      // Add a className when an element is dragged over 
      // your page.
      entry.target.classList
          .toggle('drag-active', entry.isActive);

      // Add a className when an element is dragged over 
      // the observed target.
      entry.target.classList
          .toggle('drag-over', entry.isTarget);
    }
  }
});

// Observe the target element.
obs.observe(target);
Enter fullscreen mode Exit fullscreen mode

There's more!

You should now know the basics of the Transmat library. But there's more! The library offers utilities for interacting with JSON-LD for a connected web, and a minimalistic drag-image to integrate with your existing drag-drop implementation.

Curious to hear what your ideas are with this barrier breaking technique!

Top comments (0)