Simple tutorial for Drag and Drop in HTML5
Drag and Drop is a the basic feature needed when talking about the application handling Media or shuffling Data. This post is a simple tutorial for Drag and Drop in HTML5 with JavaScript.
Drag and Drop is an essential feature when talking about the application handling Media or shuffling Data.
Today we will look at the simple tutorial for Drag and Drop in HTML5.
If you are dealing with Media, you will need a Drop area (Dropzone) in the application and the case of Data Shuffling, both Draggable elements and Dropzone.
An element can be enabled to be dragged with dragable
attribute to the element as follows:
<div draggable="true" class="card">
...
</div>
With this attribute being present with the element, you can register the event handlers for the following events:
HTML Drag and Drop API - Web APIs | MDN
drag
dragstart
dragend
dragenter
dragover
dragleave
dragexit
drop
As the element mentioned above is draggable, you can listen for dragger and drop events on the target element. As follows, in addition to the above source:
<div class="draggable-items">
<div class="row">
<div class="col">
<div class="card bg-light my-3" draggable="true">
<div class="card-header">Light card title</div>
<div class="card-body">
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
</div>
</div>
...
</div>
</div>
</div>
<div class="row dropzones">
<div class="col">
<div class="my-3 p-3 border rounded dropzone">
<span class="text-muted">Drop the cards here</span>
</div>
</div>
...
</div>
And the JS to make it work will go as follows:
const dropzones = document.querySelector('.dropzones');
let el = null;
document
.querySelector('.draggable-items')
.addEventListener('dragstart', e => {
el = e.target.cloneNode(true)
el.removeAttribute('draggable');
})
dropzones.addEventListener('dragover', (e) => {
e.preventDefault();
})
dropzones.addEventListener('dragenter', (e) => {
if (e.target.classList.contains('dropzone')) {
e.target.classList.add('solid-border');
}
})
dropzones.addEventListener('drop', (e) => {
e.preventDefault();
e.target.appendChild(el);
el = null;
e.target.classList.remove('solid-border');
})
dropzones.addEventListener('dragleave', (e) => {
if (e.target.classList.contains('dropzone')) {
e.target.classList.remove('solid-border');
}
})
The key thing to note here is that to allow drag
event to happen on the dropzone element, you need to capture dragover
event and prevent the events’ default action by event.preventDefault()
Then in the drop event, you can access the data transfer attribute from the event and take necessary action.
And For files, you need to capture dragenter
, dragover
, dragleave
and drop
events on the dropzone element.
In the case of files, the drop event has files
property in the dataTransfer
property of event, and then you can access the files in the similar way you would handle input type file
The following JS code will handle the file drops in a dropzone:
const events = [
'dragenter',
'dragleave',
'dragover', // to allow drop
'drop'
];
events.forEach(e => {
fileDropZone.addEventListener(e, (ev) => {
ev.preventDefault();
if (ev.type === 'dragenter') {
fileDropZone.classList.add('solid-border');
}
if (ev.type === 'dragleave') {
fileDropZone.classList.remove('solid-border');
}
if(ev.type === 'drop') {
fileDropZone.classList.remove('solid-border');
handleFiles(ev.dataTransfer.files)
.then(values => values.map(tag => {
tag.setAttribute('class', 'border rounded img-preview');
fileDropZone.appendChild(tag)
}));
}
})
})
const handleFiles = (_files) => {
const files = Array.prototype.slice.call(_files);
return Promise.all(files.map(imgForFile))
}
const generatePreviewData = (file) => {
const fr = new FileReader();
return new Promise((resolve, reject) => {
fr.addEventListener('load', (e) => {
resolve(fr.result);
});
fr.addEventListener('error', (e) => {
reject();
});
fr.readAsDataURL(file);
});
}
const imgForFile = (file) => {
return generatePreviewData(file)
.then((data) => {
const img = document.createElement('img');
img.src = data;
img.height = 200;
return img;
})
}
Here in the demo or example, the asynchronous operation generates the preview of the images. You can also use it to upload files to the Backend.
For the image preview, we have used the code from the previous post:
⚠️ Attention
Thin dataTransfer
While setting dataTransfer
items, keep this info as less as possible.
Why? As the events are propagated to the document root and if other delegated handlers are attached, the event processing might slow down.
preventDefault
Another critical thing to notice is to preventDefault
actions, as the browser might react to file drops or link drops and result in unwanted behaviours.
Let me know through comments ? or on Twitter at @heypankaj_ and/or @time2hack
If you find this article helpful, please share it with others ?
Subscribe to the blog to receive new posts right in your inbox.