Kanban Drag and drop
Vanilla JS, flex columns (no column dragging)
by hiddenloop
HTML
<div class="container">
<div class="column" id="col0" data-url="url0">
<div class="item" draggable="true" id="wash">Wash Clothes</div>
<div class="item" draggable="true" id="meet">Meeting at 9AM</div>
<div class="item" draggable="true" id="fix">Fix workshop</div>
<div class="item" draggable="true" id="visit">Visit the zoo</div>
</div>
<div class="column" id="col1" data-url="url1">
</div>
<div class="column" id="col2" data-url="url2">
</div>
<div class="column" id="col3" data-url="url3">
</div>
<div class="column" id="col4" data-url="url4">
</div>
</div>
CSS
.container {
font-family: sans-serif;
display: flex;
gap: 20px;
}
.column {
background: #ddd;
min-height: 90vh;
min-width: 100px;
width: 240px;
padding: 20px;
border-radius: 10px;
}
.column h1 {
text-align: center;
font-size: 16px;
}
.item {
background: #fff;
margin: 20px;
padding: 20px;
width: 60px;
border-radius: 3px;
cursor: pointer;
}
.ghosted {
background-color: rgb(255, 222, 222, 0.4);
outline: 3px dashed #CCC;
}
JavaScript
const items = document.querySelectorAll('.item')
const columns = document.querySelectorAll('.column')
let dragItem = null
let currentColumn = null
let currentSiblings = [];
function dragStart() {
//console.log('drag started');
// set current item
dragItem = this;
// hide it with a fade (ghost it)
this.classList.add("ghosted")
}
function dragEnd() {
//console.log('drag ended');
// show it back again after hiding (unghost it)
this.classList.remove("ghosted")
// clear current item
dragItem = null;
}
items.forEach(item => {
// setup events for items to drag
item.addEventListener('dragstart', dragStart)
item.addEventListener('dragend', dragEnd)
});
function dragOver(e) {
// prevent the default so we can handle our own drag drop event
e.preventDefault()
if (!dragItem) {
return
}
if (currentColumn != this) {
//console.log("setting new col as current")
currentColumn = this
currentSiblings = Array.from(currentColumn.children)
}
//console.log('drag over ' + currentColumn.id);
const nextElement = findSubsequentElement(event.clientX, event.clientY)
if (nextElement) {
//console.log("moving before: " + nextElement.id)
currentColumn.insertBefore(dragItem, nextElement)
} else {
currentColumn.append(dragItem)
}
}
function findSubsequentElement(x, y) {
for (const sibling of currentSiblings) {
if (sibling !== dragItem) {
const rect = sibling.getBoundingClientRect()
const midX = rect.left + (rect.width / 2)
if (y >= rect.top && y <= rect.bottom) {
if (x < midX) {
return sibling
}
}
if (y < rect.top) {
return sibling
}
}
}
return null
}
function dragDrop() {
//console.log('drag dropped');
//this.append(dragItem);
const position = Array.from(currentColumn.children).indexOf(dragItem)
console.log('at position ' + position);
submitMoveRequest(currentColumn.dataset.url, dragItem.id,...