Drag And Drop API


Drag and drop (DnD) is a first class citizen in HTML5! The spec defines an event-based mechanism, JavaScript API, and additional markup for declaring that just about any type of element be draggable on a page

Creating draggable content

Making an object draggable is simple. Set the draggable=true attribute on the element you want to make moveable. Just about anything can be drag-enabled, including images, links, files, or other DOM nodes.

The basic markup may look something like this:

id="columns">
class="column" draggable="true">
A
class="column" draggable="true">
B
class="column" draggable="true">
C
</div>

For example, dragging the logo on google.com produces a ghost image:

Dragging an image in the browser

Listening for Dragging Events

There are a number of different events to attach to for monitoring the entire drag and drop process:

  • dragstart
  • drag
  • dragenter
  • dragleave
  • dragover
  • drop
  • dragend

1. Starting a Drag

Once you have draggable="true" attributes defined on your content, attachdragstart event handlers to kick off the DnD sequence for each column.

This code will set the column’s opacity to 40% when the user begins dragging it:

function handleDragStart(e) {
  this.style.opacity = '0.4';  // this / e.target is the source node.
}

var cols = document.querySelectorAll('#columns .column');
[].forEach.call(cols, function(col) {
  col.addEventListener('dragstart', handleDragStart, false);
});

2. dragenter, dragover, and dragleave

dragenter, dragover, and dragleave event handlers can be used to provide additional visual cues during the drag process. For example, when a column is hovered over during a drag, its border could become dashed. This will let users know the columns are also drop targets.

<style>
.column.over {
  border: 2px dashed #000;
}
</style>
function handleDragStart(e) {
  this.style.opacity = '0.4';  // this / e.target is the source node.
}

function handleDragOver(e) {
  if (e.preventDefault) {
    e.preventDefault(); // Necessary. Allows us to drop.
  }

  e.dataTransfer.dropEffect = 'move';  // See the section on the DataTransfer object.

  return false;
}

function handleDragEnter(e) {
  // this / e.target is the current hover target.
  this.classList.add('over');
}

function handleDragLeave(e) {
  this.classList.remove('over');  // this / e.target is previous target element.
}

var cols = document.querySelectorAll('#columns .column');
[].forEach.call(cols, function(col) {
  col.addEventListener('dragstart', handleDragStart, false);
  col.addEventListener('dragenter', handleDragEnter, false);
  col.addEventListener('dragover', handleDragOver, false);
  col.addEventListener('dragleave', handleDragLeave, false);
});

There are a couple of points worth covering in this code:

  • The this/e.target changes for each type of event, depending on where we are in the DnD event model.
  • In the case of dragging something like a link, we need to prevent the browser’s default behavior, which is to navigate to that link. To do this, call e.preventDefault() in the dragover event. Another good practice is to return false in that same handler. Browsers are somewhat inconsistent about needing these, but they don’t hurt to add.
  • dragenter is used to toggle the ‘over’ class instead of the dragover. If we were to use dragover, our CSS class would be toggled many times as the event dragover continued to fire on a column hover. Ultimately, that would cause the browser’s renderer to do a large amount of unnecessary work. Keeping redraws to a minimum is always a good idea.

3. Completing a Drag

To process the actual drop, add an event listener for the drop and dragend events. In this handler, you’ll need to prevent the browser’s default behavior for drops, which is typically some sort of annoying redirect. You can prevent the event from bubbling up the DOM by calling e.stopPropagation().

The DataTransfer object

The dataTransfer property is where all the DnD magic happens. It holds the piece of data sent in a drag action. dataTransfer is set in the dragstart event and read/handled in the drop event. Calling e.dataTransfer.setData(format, data) will set the object’s content to the mimetype and data payload passed as arguments.

Dragging properties

The dataTransfer object exposes properties to provide visual feedback to the user during the drag process. These properties can also be used to control how each drop target responds to a particular data type.

dataTransfer.effectAllowed
Restricts what ‘type of drag’ the user can perform on the element. It is used in the drag-and-drop processing model to initialize the dropEffect during thedragenter and dragover events. The property can be set to the following values: none, copy, copyLink, copyMove, link, linkMove, move, all, and uninitialized.
dataTransfer.dropEffect
Controls the feedback that the user is given during the dragenter anddragover events. When the user hovers over a target element, the browser’s cursor will indicate what type of operation is going to take place (e.g. a copy, a move, etc.). The effect can take on one of the following values: none, copy, link, move.
    e.dataTransfer.setDragImage(imgElement, x, y)
Instead of using the browser’s default ‘ghost image’ feedback, you can optionally set a drag icon

var dragIcon = document.createElement('img');
dragIcon.src = 'logo.png';
dragIcon.width = 100;
e.dataTransfer.setDragImage(dragIcon, -10, -10);

Conclusion

No one will argue that HTML5’s DnD model is complicated compared to other solutions like JQuery UI. However, any time you can take advantage of the browser’s native APIs, do so! After all, that’s the whole point of HTML5…which is to standardize and make available a rich set of APIs that are native to the browser.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s