JSFiddle - React, Tailwind, and code Playground

by Tony Realovich

HTML

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Drag & Drop</title>
</head>
<body>
  <ul class="list">
    <li class="item">
      <div class="label">First</div>
    </li>
    <li class="item">
      <div class="label">Second</div>
      <ul class="sublist">
        <li class="subitem"><div class="sublabel">SubFirst</div></li>
        <li class="subitem"><div class="sublabel">SubSecond</div></li>
        <li class="subitem"><div class="sublabel">SubThird</div></li>
        <li class="subitem"><div class="sublabel">SubFourth</div></li>
      </ul>
    </li>
    <li class="item">
      <div class="label">Third</div>
    </li>
    <li class="item">
      <div class="label">Fourth</div>
    </li>
  </ul>
</body>
</html>

CSS

.label {
  padding: 8px;
  border: 2px solid LightSteelBlue;
  background-color: LightBlue;
  border-radius: 8px;
  color: SteelBlue;
  font-weight: bold;
  text-transform: uppercase;
}

.sublabel {
  padding: 4px 8px;
  border: 2px dotted BurlyWood;
  background-color: Bisque;
  border-radius: 4px;
  color: Peru;
  font-weight: bold;
}

li {
  margin: 4px 0;
}

.selected {
  opacity: .6;
}

JavaScript

const dndListElement = document.querySelector('.list');
const dndItemClass = 'item';

const dndSublistElement = document.querySelector('.sublist');
const dndSubitemClass = 'subitem';

const makeDraggable = (itemClass, listElement) => {
  const items = document.querySelectorAll(`.${itemClass}`);

  for (const dndItem of items) {
    dndItem.draggable = true;
  }
  
  listElement.addEventListener('dragstart', (evt) => {
    evt.target.classList.add('selected');
  }); 
  
  listElement.addEventListener('dragend', (evt) => {
    evt.target.classList.remove('selected');
  });
  
  listElement.addEventListener('dragover', (evt) => {
    evt.preventDefault();
  
    const activeElement = listElement.querySelector('.selected');
    const currentElement = evt.target;
    const parentElement = currentElement.parentElement;
  
    const isMoveable = activeElement !== parentElement && parentElement.classList.contains(`${itemClass}`);
    
    if(!isMoveable) {
      return;
    }
  
    const nextElement = (parentElement === activeElement.nextElementSibling) ? parentElement.nextElementSibling : parentElement;
  
    listElement.insertBefore(activeElement, nextElement);
  });
}

makeDraggable(dndItemClass, dndListElement);
makeDraggable(dndSubitemClass, dndSublistElement);