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,...