A simple and lightweight JavaScript library for reorderable drag-and-drop lists or items. It uses the HTML Drag and Drop API which is sadly not supported by touch devices.
You may visit the docs.tobento.ch/js-sortable page for demo.
Modern browser only. No touch devices.
1. Include JS
<script src="sortables.js" type="module"></script>
2. Register
Use the data-sortable
attribute to automatically register a sortable.
<ul data-sortable='{"id": "uniqueID", "selector": "li", "nestable": true}'>
<li><div class="box">1 - List Item</div></li>
<li><div class="box">2 - List Item</div></li>
<li><div class="box">3 - List Item</div></li>
<li><div class="box">4 - List Item</div></li>
<li><div class="box">5 - List Item</div>
<ul>
<li><div class="box">5.1 - List Item</div></li>
<li><div class="box">5.2 - List Item</div></li>
</ul>
</li>
<li><div class="box">6 - List Item</div></li>
<li><div class="box">7 - List Item</div></li>
<li><div class="box">8 - List Item</div></li>
</ul>
<div data-sortable='{"id": "cards", "selector": ".card", "handle": ".handle"}'>
<div class="card">1 - Card<span class="handle">Drag</span></div>
<div class="card">2 - Card<span class="handle">Drag</span></div>
<div class="card">3 - Card<span class="handle">Drag</span></div>
<div class="card">4 - Card<span class="handle">Drag</span></div>
</div>
Thats all.
You may get the sorted items
<script type="module">
import sortables from "sortables.js";
document.addEventListener('DOMContentLoaded', (e) => {
const items = sortables.get('uniqueID').items();
// or using the drop event
sortables.get('uniqueID').listen('drop', (event, sortable) => {
const items = sortable.items();
});
});
</script>
Manually creating
Instead of using the data-sortable
attribute to register the sortable automatically, you can create sortables manually by using the create
function:
<script type="module">
import sortables from "sortables.js";
document.addEventListener('DOMContentLoaded', (e) => {
// create it manually:
const sortable = sortables.create(document.querySelector('#container'), {
id: 'uniqueID',
selector: '.item'
});
// you may get the sorted items using the drop event:
sortable.listen('drop', (event, sortable) => {
const items = sortable.items();
});
});
</script>
<ul data-sortable='{"id": "uniqueID", "selector": "li", "nestable": true, "handle": ".drag"}'>
Option | Value | Description |
---|---|---|
"id" |
"ID" |
A unique id. |
"selector" |
".item" |
The items selector |
"nestable" |
true or false |
When true, items can be nested if "li" selector. But you may set to false if you want only a one depth list. (optional) |
"handle" |
.handle |
A handle selector to drag items only with. (optional) |
"allow" |
["id", "anotherId"] |
You may set the allowed ids to be sorted within. (optional) |
"clone" |
true or false |
When true, the drag item will be cloned. (optional) |
"ghost" |
true or false |
When false, the ghost image will not be displayed. (optional) |
<script type="module">
import sortables from "sortables.js";
document.addEventListener('DOMContentLoaded', (e) => {
// create a sortable object:
const sortable = sortables.create(document.querySelector('#container'), {
id: 'ID',
selector: '.item'
});
// you may get a sortable object by id:
const sortable = sortable.get('ID');
// you may check if a sortable object exists:
if (sortable.has('ID')) {
//
}
// you may get the sortable items:
const items = sortable.items();
// you may delete a sortable:
sortables.delete('ID');
// you may register newly added sortables with the data-sortable attribute:
sortables.register();
});
</script>
Event | Description |
---|---|
dragstart |
This event is fired when the user starts dragging an element. |
dragend |
This event is fired when a drag operation ends. |
dragover |
This event is fired when an element is being dragged over a valid drop target. |
dragleave |
This event is fired when a dragged element leaves a valid drop target. |
drop |
This event is fired when an element is dropped on a valid drop target. |
sortables.get('uniqueID').listen('drop', (event, sortable) => {
const items = sortable.items();
});
This example shows a possible way to update a reoredered list.
<script type="module">
import sortables from "sortables.js";
document.addEventListener('DOMContentLoaded', (e) => {
const sortable = sortables.get('list');
sortable.listen('drop', (event, sortable) => {
// Get the ul element where the item was dropped:
const ul = sortable.draggable.closest('ul');
// Determine the parent id:
let parentId = 0;
const parent = ul.parentNode;
if (parent && parent.hasAttribute('data-id')) {
parentId = parent.getAttribute('data-id');
}
// Get only those li elements within the current ul element (without children):
const items = ul.querySelectorAll(':scope > li');
// Build your data structure to update your items using the Fetch API.
const data = {};
const length = items.length;
for (let i = 0; i < length; i++) {
data[i] = {};
data[i]['id'] = items[i].getAttribute('data-menu-id');
data[i]['parent_id'] = parentId;
data[i]['sortorder'] = i+1;
}
console.log(data);
});
});
</script>
<ul data-sortable='{"id": "list", "selector": "li", "nestable": true}'>
<li data-id="1"><div class="box">1 - List Item</div></li>
<li data-id="2"><div class="box">2 - List Item</div></li>
<li data-id="3"><div class="box">3 - List Item</div></li>
<li data-id="4"><div class="box">4 - List Item</div></li>
<li data-id="5"><div class="box">5 - List Item</div>
<ul>
<li data-id="9"><div class="box">5.1 - List Item</div></li>
<li data-id="10"><div class="box">5.2 - List Item</div></li>
</ul>
</li>
<li data-id="6"><div class="box">6 - List Item</div></li>
<li data-id="7"><div class="box">7 - List Item</div></li>
<li data-id="8"><div class="box">8 - List Item</div></li>
</ul>
To support touch devices you may consider a polyfill such as the Dragdroptouch.