HTML5 now defines a direct API for handling drag and drop (“D&D”). This API allows us to specify that certain elements are draggable, and then specify what should happen when these draggable elements are dragged over or dropped onto other elements on the page.
Drag and Drop is supported in:
To test if the browser supports native D&D functionality, use the following feature detect.
var dnd_support = 'draggable' in document.createElement('span');
HTML5 spec added some new content to the API. Enabling any element to be dragged is incredibly easy. Take your div and add the new attribute: draggable
.
<div draggable="true">This element be draggable</div>
DataTransfer objects are one of the new objects outlined in the Drag and Drop API. These objects allow us to set and get data about the objects that are being dragged
DataTransfer lets us define two pieces of information.
By using the setData and getData methods on the dataTransfer
object, you can pass data from elements inside our application to other pages of our app, or across browser windows
img.ondragstart = function(event) { event = event || window.event; // here be one long line event.dataTransfer.setData('text / plain', 'This is the screen name for' +this.getAttribute('data - screen_name') + ', whose image can be found here: '+this.src); };
In total, there are seven drag and drop events.
Properties | Description |
---|---|
dragenter | |
dragover | Tell the browser this is an element that accepts drop data. |
drop | Once something has been dropped on the element, do something with the dropped data. |
dragstart | |
dragend | (the complement to dragstart) |
dragenter | |
dragleave |
The enter and leave events fire on the dropzone as the dragged item enters the element.
--<style> #foobar { background-color:yellow; width:100px; height:100px; cursor:move; } #catcher { background-color:blue; width:150px; height:150px; padding:5px; margin-bottom:5px; } </style>
Put the draggable attribute on the element you want to be drag-enabled:
<div id="catcher">...</div> <div id="foobar" draggable="true">...</div>
We need to use the JavaScript API and D&D events to tell the browser where the element can be dragged to, and what to do once it's dropped there.
For example, we can listen for the dragstart event, and style the element differently when it's being dragged (like putting a border around it or making it partially transparent).
var foobar = document.getElementById("foobar"); foobar.addEventListener("dragstart", function(evt) { this.style.border = "3px dotted #000"; // black dotted-line border }, false);
Lets style an element that can receive the drop, so that when the dragged item is over it, it makes it obvious that you can drop the element there (as opposed to just dropping it in any location):
var catcher = document.getElementById("catcher"); // will catch the dropped element catcher.addEventListener("dragenter", function(evt) { this.style.border = "3px solid red"; // make the catcher have a red border }, false); catcher.addEventListener("dragleave", function(evt) { this.style.border = ""; // remove the border from the catcher }, false); catcher.addEventListener("dragover", function(evt) { if (evt.preventDefault) evt.preventDefault(); return false; }, false);
Add event listeners to the element that catch our dropped element for the dragover, dragenter, and dragleave events.
dragenter and dragleave - events simply toggle on a red border to our target element to make it clear that you can drop the element there.
dragover - event is fired continuously while dragging around on top of the target.
So we would not want to toggle on the red border in that handler, as that would create unnecessary work for the browser. However, we do need to prevent that event from various default behavior, depending on the type of element being dragged. This is why we use preventDefault()
and return false.
Modify our dragstart event handler to wire up a dataTransfer object with data that the browser needs for handling the D&D actions
foobar.addEventListener("dragstart", function(evt) { this.style.border = "3px dotted #000"; // black dotted-line border evt.dataTransfer.effectAllowed = "move"; evt.dataTransfer.setData("Text", this.id); }, false);
effectAllowed property - controls what visual feedback—generally the mouse cursor—the browser gives on the type of drag event that is occurring (move, copy, etc.).
setData(...) - method tells the D&D mechanism in the browser which data from the element being dragged should be dropped into the target element, otherwise known as the drop catcher. Here we specify that only the id property of the original element is transferred, which is used later to actually move the element.
Define a dragend event handler to clear up the visuals, and a drop event handler, to actually do the moving of our element:
foobar.addEventListener("dragend", function(evt) { this.style.border = ""; // remove the border }, false); catcher.addEventListener("drop", function(evt) { if (evt.preventDefault) evt.preventDefault(); if (evt.stopPropagation) evt.stopPropagation(); this.style.border = ""; // remove the border from the catcher var id = evt.dataTransfer.getData("Text"); // get the id var elem = document.getElementById(id); elem.parentNode.removeChild(elem); // remove the element this.appendChild(elem); // add the element back into our catcher return false; }, false);
The drop event handler, we first get the data that was transferred in the drop, which in this case was the id property of the original source element that we dragged. Next, we remove that element from its current location, and finally add it back into the new location inside our catcher container.