Kanban Drag and drop
Vanilla JS, flex columns (with column dragging)
by hiddenloop
HTML
<div class="container" data-url="columnMoveUrl">
<div class="column" id="col0" data-url="itemMoveUrl0" draggable="true">
<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="itemMoveUrl1" draggable="true">
</div>
<div class="column" id="col2" data-url="itemMoveUrl2" draggable="true">
</div>
<div class="column" id="col3" data-url="itemMoveUrl3" draggable="true">
</div>
<div class="column" id="col4" data-url="itemMoveUrl4" draggable="true">
</div>
</div>
CSS
.container {
font-family: sans-serif;
display: flex;
gap: 20px;
}
.column {
background: #ddd;
min-height: 90vh;
min-width: 140px;
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;
}
.ghosted > .item {
display: none;
}
JavaScript
const items = document.querySelectorAll('.item')
const columns = document.querySelectorAll('.column')
let dragItem = null
let currentColumn = null
let currentSiblings = [];
function dragStart(e) {
//console.log('drag started');
// set current item
dragItem = this;
// hide it with a fade (ghost it)
this.classList.add("ghosted")
e.stopPropagation();
}
function dragEnd(e) {
//console.log('drag ended');
// show it back again after hiding (unghost it)
this.classList.remove("ghosted")
// clear current item
dragItem = null;
e.stopPropagation();
}
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(dragItem.classList.contains("column")) {
console.log("dragging a col")
currentColumn = this.parentElement
currentSiblings = Array.from(currentColumn.children)
} else 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(currentSiblings, event.clientX, event.clientY)
if (nextElement) {
//console.log("moving before: " + nextElement.id)
currentColumn.insertBefore(dragItem, nextElement)
} else {
currentColumn.append(dragItem)
}
}
function findSubsequentElement(inElements, x, y) {
for (const sibling of inElements) {
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...