Simple sortable solution for a list of items using vanilla javascript
I needed to create a very simple solution for sorting a list of items.
I started with a list, each element having it's own id
<ul class="simple-sortable">
<li data-id="1">1 Bob</li>
<li data-id="2">2 Frank</li>
<li data-id="3">3 Sam</li>
<li data-id="4">4 Zack</li>
</ul>
Next I had a look at the Drag and Drop API
Here I discovered the events that I could use for my simple example: dragstart, dragenter and dragend.
In order to use these events, I needed to set draggable
on each of the list items.
I decided to set the attribute using javascript:
item.setAttribute("draggable", true);
dragstart
item.addEventListener("dragstart", (e) => {
dragged = e.target;
})
When this event fires you know that e.target
is the element that is being dragged.
dragenter
item.addEventListener("dragenter", (e) => {
if (dragged !== e.target) {
target = e.target;
}
})
dragenter
fires when you touch elements of the list.
e.target
is the element that is being touched by your dragged
element.
dragend
item.addEventListener("dragend", (e) => {
if (target) {
if (draggedY < target.getBoundingClientRect().y) {
dragged.parentNode.removeChild(dragged);
target.after(dragged);
} else {
dragged.parentNode.removeChild(dragged);
target.before(dragged);
}
}
})
Finally on dragend you can start to rearrange the elements that are affected by the new order
All together now
And the js
function SimpleSortable(newOrderHandler) {
window.addEventListener("load", () => {
let dragged;
let draggedY;
let target;
let selectorItems = ".simple-sortable li";
const list = document.querySelectorAll(selectorItems);
list.forEach((item) => {
item.setAttribute("draggable", true);
item.addEventListener("dragstart", (e) => {
dragged = e.target;
draggedY = dragged.getBoundingClientRect().y;
dragged.style.border = "1px solid #000";
});
item.addEventListener("dragenter", (e) => {
if (dragged !== e.target) {
target = e.target;
}
});
item.addEventListener("dragend", (e) => {
e.preventDefault();
if (target) {
if (draggedY < target.getBoundingClientRect().y) {
dragged.parentNode.removeChild(dragged);
target.after(dragged);
} else {
dragged.parentNode.removeChild(dragged);
target.before(dragged);
}
}
dragged.style.border = "0";
let newOrder = [];
document.querySelectorAll(selectorItems).forEach(a => {
newOrder.push(a.dataset.id);
});
newOrderHandler(newOrder);
}, false);
});
});
}
I ended up creating a small library - https://github.com/stefanscript/simple-sortable.
References:
Drag and drop MDN - https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API