Welcome to a quick tutorial on how to create a drag-and-drop sortable list with HTML and Javascript. Once upon a time in the Dark Ages of the Internet, implementing drag-and-drop is a massive task that involves a lot of coding with 3rd party libraries. But ever since HTML5, it has been made a native feature and we no longer have to fight with digital dragons.
In the simplest design, drag-and-drop in HTML and Javascript only requires:
- Create draggable items by attaching the draggable property –
<div draggable>Drag This</div>
- Define the dropzone –
<div id="drop">Drop Here</div>
- Attach a drop listener in Javascript –
document.getElementById("drop").ondrop = () => { DO SOMETHING };
That covers the basics, but how can we create a sortable list with this? Read on for an example!
TABLE OF CONTENTS
DRAG-AND-DROP SORTABLE LIST
All right, let us now get into the details of how the drag-and-drop list works.
SORTABLE LIST DEMO
- First
- Second
- Third
- Forth
- Fifth
STEP 1) THE HTML
<!-- (A) LOAD CSS + JS -->
<link rel="stylesheet" href="2-sort-list.css">
<script src="3-sort-list.js"></script>
<!-- (B) THE LIST -->
<ul id="sortlist">
<li>First</li>
<li>Second</li>
<li>Third</li>
<li>Forth</li>
<li>Fifth</li>
</ul>
<!-- (C) CREATE SORTABLE LIST -->
<script>
slist(document.getElementById("sortlist"));
</script>
For you guys who just want to use this as a “plugin” without reading the rest of the tutorial:
- Load the CSS and Javascript. Doh.
- Define the
<ul>
or<ol>
as usual. Take note that this simple example will only work with a flat one-level list. - Use the
slist(TARGET)
function on window load to initiate the sortable list.
STEP 2) THE CSS
/* (A) LIST STYLES */
.slist {
list-style: none;
padding: 0;
margin: 0;
}
.slist li {
margin: 10px;
padding: 15px;
border: 1px solid #dfdfdf;
background: #f5f5f5;
}
/* (B) DRAG-AND-DROP HINT */
.slist li.hint {
border: 1px solid #ffc49a;
background: #feffb4;
}
.slist li.active {
border: 1px solid #ffa5a5;
background: #ffe7e7;
}
These are just some simple cosmetic styles for the sortable list.
- The Javascript will attach a
.slist
class to the container. In this section, we remove the default bullet points and make the list look a little better. - The
.hint
class is used to highlight the possible dropzones, and.active
when the draggable is hovering over the dropzone.
STEP 3) THE JAVASCRIPT
function slist (target) {
// (A) SET CSS + GET ALL LIST ITEMS
target.classList.add("slist");
let items = target.getElementsByTagName("li"), current = null;
// (B) MAKE ITEMS DRAGGABLE + SORTABLE
for (let i of items) {
// (B1) ATTACH DRAGGABLE
i.draggable = true;
// (B2) DRAG START - YELLOW HIGHLIGHT DROPZONES
i.ondragstart = e => {
current = i;
for (let it of items) {
if (it != current) { it.classList.add("hint"); }
}
};
// (B3) DRAG ENTER - RED HIGHLIGHT DROPZONE
i.ondragenter = e => {
if (i != current) { i.classList.add("active"); }
};
// (B4) DRAG LEAVE - REMOVE RED HIGHLIGHT
i.ondragleave = () => i.classList.remove("active");
// (B5) DRAG END - REMOVE ALL HIGHLIGHTS
i.ondragend = () => { for (let it of items) {
it.classList.remove("hint");
it.classList.remove("active");
}};
// (B6) DRAG OVER - PREVENT THE DEFAULT "DROP", SO WE CAN DO OUR OWN
i.ondragover = e => e.preventDefault();
// (B7) ON DROP - DO SOMETHING
i.ondrop = e => {
e.preventDefault();
if (i != current) {
let currentpos = 0, droppedpos = 0;
for (let it=0; it<items.length; it++) {
if (current == items[it]) { currentpos = it; }
if (i == items[it]) { droppedpos = it; }
}
if (currentpos < droppedpos) {
i.parentNode.insertBefore(current, i.nextSibling);
} else {
i.parentNode.insertBefore(current, i);
}
}
};
}
}
Right, the Javascript looks like quite a handful at first, but keep calm and study closely.
- Attach a
.slist
CSS class to the list, and get all the<li>
list items. - Loop through all the <li>, attach a load of drag-and-drop listeners.
- (B1) Set
<li draggable>
. - (B2) On drag start, attach
.hint
to highlight all the list items. - (B3) When the dragged element hovers a list item, add
.active
to show a different highlight color. - (B4) When the dragged element leaves a list item, remove
.active
. - (B5) When the drag stops, remove all
.hint
and.active
CSS classes. - (B6) Necessary. Prevents the default browser action, so we can define our own.
- (B7) Some Math. Does the actual sorting on dropped.
- (B1) Set
DOWNLOAD & NOTES
Here is the download link to the example code, so you don’t have to copy-paste everything.
SORRY FOR THE ADS...
But someone has to pay the bills, and sponsors are paying for it. I insist on not turning Code Boxx into a "paid scripts" business, and I don't "block people with Adblock". Every little bit of support helps.
Buy Me A Coffee Code Boxx eBooks
EXAMPLE CODE DOWNLOAD
Click here to download the source code. I have released it under the MIT license, so feel free to build on top of it or use it in your own project.
EXTRA BITS & LINKS
That’s it for the example code, and here are some extras and links that you may find useful.
COMPATIBILITY CHECKS – NOTES ON MOBILE SUPPORT
- Arrow Functions – CanIUse
- Draggable – CanIUse
- Drag and Drop – CanIUse
Guess what? Mobile devices and browsers still don’t properly support drag-and-drop at the time of writing. If you have to do drag-and-drop on mobile devices, the best bet is to use a “polyfill” such as DragDropTouch and MobileDragDrop.
THE SUMMARY – DRAG-DROP EVENTS
Event | Description |
dragstart |
Fired when the drag starts. |
dragend |
Fired when the drag stops. |
drag |
As the element is being dragged around. |
dragenter |
When the mouse enters the boundaries of an element. |
dragover |
As the element is being dragged over another element. |
dragleave |
When the mouse exits the boundaries of an element. |
drop |
Fired when the element is being dropped. |
LINK & REFERENCES
- HTML draggable – MDN
- HTML Drag and Drop API – MDN
TUTORIAL VIDEO
THE END
Thank you for reading, and we have come to the end of this guide. I hope that it has helped you to better understand how vanilla Javascript drag and drop works. If you have anything to add to this guide, please feel free to comment below. Good luck and happy coding!
For anyone to save the position with reload the browser, use this:
/////////////// To Save and Show data ////////////////////////
let your-variable = document.getElementById(“your-id”);
function saveData(){
localStorage.setItem(“data”, your-variable.innerHTML);
}
function showTask(){
your-variable.innerHTML = localStorage.getItem(“data”);
}
showTask();
/////////////// In drap and drop – take care of “saveData();” /////////////////
///// B5 /////
// (B5) DRAG END – REMOVE ALL HIGHLIGHTS
i.ondragend = () => { for (let it of items) {
it.classList.remove(“hint”);
it.classList.remove(“active”);
saveData();
}};
///// B7 /////
// (B7) ON DROP – DO SOMETHING
i.ondrop = e => {
e.preventDefault();
if (i != current) {
let currentpos = 0, droppedpos = 0;
for (let it=0; it<items.length; it++) {
if (current == items[it]) { currentpos = it; }
if (i == items[it]) { droppedpos = it; }
}
if (currentpos < droppedpos) {
i.parentNode.insertBefore(current, i.nextSibling);
saveData();
} else {
i.parentNode.insertBefore(current, i);
saveData();
}
}
I see it’s not working with mobile view, can you please advise if it could be possible to fix?
As above, see “COMPATIBILITY CHECKS – NOTES ON MOBILE SUPPORT”.