3.3 Drag and drop
Add ondragstart
event handler to the containing html element and add draggable="true"
attribute to its children html elements.
<ol ondragstart="dragStartHandler(event)">
<li draggable="true" data-value="fruit-apple">Apples</li>
<li draggable="true" data-value="fruit-orange">Oranges</li>
<li draggable="true" data-value="fruit-pear">Pears</li>
</ol>
It's the draggable children that will fire the events (similar to click bubbling/delegation).
Detecting drop and using the dragged elements
data-value
attribute of the draggable elements and copies it to the "drag and drop clipboard" to use it later. This is the event.dataTransfer.setData(target_type, value
)
:
function dragStartHandler(event) {
console.log('dragstart event, target: ' + event.target.innerHTML);
// Copy to the drag'n'drop clipboard the value of the
// data* attribute of the target,
// with a type "Fruit".
event.dataTransfer.setData("Fruit", event.target.dataset.value);
}
The "drop zone"
It's an HTML element with an ondrop
event handler
<div ondragover="return false" ondrop="dropHandler(event);">
Drop your favorite fruits below:
<ol id="droppedFruits"></ol>
</div>
The ondragover="return false"
will avoid propagating dragover
events that may occur in high number
The drop handler will use the content from "clipboard"
using the event.dataTransfer.getData(target_type)
method;
function dropHandler(event) {
console.log('drop event, target: ' + event.target.innerHTML);
...
// get the data from the drag'n'drop clipboard,
// with a type="Fruit"
var data = event.dataTransfer.getData("Fruit");
// do something with the data
...
}
Adding visual feedback
dragstart
and dragend
can be used to toggle CSS classes on the html elementsdragenter
entering the drop zonedragleave
leaving the drop zone (moves out, set back style to original)dragover
best practice is to prevent propagation of the event and also prevent the default behaviour of the browser (i.e. open image on a new tab)drop
where we can process the drop (get the value from the clipoard). also resetting the styling of the drop zone
Visual feedback on the cursor
To give this visual feedback, we use the effectAllowed and dropEffect properties of the dataTransfer object. To set one of the possible predefined cursors, we allow an effect in the dragstart handler, and we set the effect (to "move", "copy", etc.) in the dragEnter or dragOver handler.
function dragStartHandler(event) {
// Allow a "copy" cursor effect
event.dataTransfer.effectAllowed = 'copy';
...
}
And here is where we can set the cursor to a permitted value:
function dragEnterHandler(event) {
// change the cursor shape to a "+"
event.dataTransfer.dropEffect = 'copy';
...
}
To set a custom image, we also do the following in the dragstart handler:
function dragStartHandler(event) {
// allowed cursor effects
event.dataTransfer.effectAllowed = 'copy';
// Load and create an image
var dragIcon = document.createElement('img');
dragIcon.src = 'anImage.png';
dragIcon.width = 100;
// set the cursor to this image, with an offset in X, Y
event.dataTransfer.setDragImage(dragIcon, -10, -10);
...
}
#####All possible values for dropEffect and effectAllowed:
dataTransfer.effectAllowed
can be set to the following values: none, copy, copyLink, copyMove, link,linkMove, move, all, and uninitialized.
dataTransfer.dropEffect
can take on one of the following values: none, copy, link, move.
Drag and drop images or any HTML element
<img>
elements are draggable by default (no need for draggable=true
attribute)- each img has an id
- copy this id to the drag'n'drop clipboard (
evt.dataTransfer.setData("Text", target.id)
) - on drop, get the id, get the img with this id and insert into the html tree: (
var id = evt.dataTransfer.getData("Text"); target.appendChild(document.getElementById(id));
)
Drag and drop a text selection
There is no need to add a dragstart handler on an element that contains text. Any selected text is automatically added to the clipboard with a name/key equal to "text/plain". Just add a drop event handler on the drop zone and get the data from the clipboard using "text/plain" as the access key:
function drop(target, event) {
event.preventDefault();
target.innerHTML = event.dataTransfer.getData('text/plain');
};