Draggable/Sortable List

by Konstantin Rouda

HTML

<section class="c-container">
  <ul class="o-list o-list--sortable jsSortableList">
    <li>1) Stewie Griffin</li>
    <li>2) Lois Griffin</li>
    <li>3) Peter Griffin</li>
    <li>4) Meg Griffin</li>
    <li>5) Chris Griffin</li>
    <li>6) Brian Griffin</li>
    <li>7) Glenn Quagmire</li>
    <li>8) Joe Swanson</li>
  </ul>
</section>

CSS

HTML {
  font-size: 100%;
  line-height: 1.5;
  box-sizing: border-box;
}

BODY {
  height: 100vh;
  margin: 0;
}

*, *::before, *::after {
  box-sizing: inherit;
  color: inherit;
}


/*Actual Style*/
.c-container {
  max-width: 350px;
  margin: 5em auto 0;
}

.o-list {
  position: relative;
  list-style: none;
  padding: 1px;
}

.o-list > LI {
  padding: .5em 1em;
  border: 1px solid;
  /*cursor: -webkit-grab;
  cursor: -moz-grab;
  cursor: grab;*/
  margin-top: 1rem;
  user-select: none;
}

.o-list--sortable > LI:hover {
  cursor: move;
}


/*helpers*/
.o-drop-placeholder {
  position: static !important;
  display: list-item;
  width: 100%;
  //visibility: hidden;
  opacity: .5;
  border: 1px dashed !important;
}

.o-list__item--draggable {
  position: absolute;
  width: 100%;
  //margin: 0 !important;
  box-shadow: 3px 3px 6px rgba(0, 0, 0, .2);
  z-index: 999;
  background: rgb(202, 241, 255);
}

JavaScript

;(function() {
  "use strict";
  
  const list = document.querySelector(".jsSortableList");
  const listItems = list.querySelectorAll("LI");
  
  const DROP_PLACEHOLDER_ELEM_CLASS = "o-drop-placeholder";
  const DROP_PLACEHOLDER_ELEM_ID = "drop-placeholder";
  const DRAGGABLE_ITEM_STYLE_CLASS = "o-list__item--draggable";
  
  const parentWidth = list.clientWidth,
        parentHeight = list.clientHeight;
  
  
  let listItemsDimensions = [];
  /// calculates list items dimensions at the beginning
  caculateListItemsDimensions();
  
  let isMouseDown = false;
  
  let mouseX, mouseY,
      elemTop, elemLeft,
      diffX, diffY;
  
  let currentDraggable = null,
      currentUpperBound,
      currentBottomBound,
      draggableHalfHeight,
      currentDraggableMarginOffset = 0;
  
  /*======================
     Add EveneListeners
  ========================*/
  
  list.addEventListener("mousedown", onMouseDown);
  list.addEventListener("mousemove", onMouseMove);
  document.addEventListener("mouseup", onMouseUp);
  
    /*======================
            Events
    ========================*/
   
  function onMouseDown(e) {

    /// prevents mouse down which fires outside the list
    if(e.target === list || e.target.parentNode !== list) return; 
    
    currentDraggable = e.target;
     
    isMouseDown = true;

    mouseX = e.clientX;
    mouseY = e.clientY;
    
    draggableHalfHeight = (currentDraggable.offsetHeight / 2);
    
    currentDraggableMarginOffset = parseInt(getComputedStyle(currentDraggable).marginTop);
     
    /// creates element for placeholder
    let dropPlaceholderElem = createDropPlaceHolder(DROP_PLACEHOLDER_ELEM_CLASS, currentDraggable);
    
    let nextElem = currentDraggable.nextElementSibling;
     
    if(nextElem) { /*puts placeholder before adjacent sibling (that is visually below the draggable element) element if it exists*/
        nextElem.parentNode.insertBefore(dropPlaceholderElem, nextElem);
    } else { /*append placeholder...