designmode
designmode

Reputation: 163

Javascript Drag, Drop and bump other elements away

I have built out basic drag and drop feature that allows us to drag randomly placed absolute positioned elements within a container. Ideally I would want to push other elements that possibly intersect the dragged elements boundaries away, but I am presently unable find a solution.

I have this for the Drag and Drop Solution:

var draggableElements = document.getElementsByClassName("team_member");

for(var i = 0; i < draggableElements.length; i++){
    dragElement(draggableElements[i]);
}

function dragElement(elmnt) {
    var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
    if (document.getElementById(elmnt.id + "header")) {
        document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
    } else {
        elmnt.onmousedown = dragMouseDown;
    }
    function dragMouseDown(e) {
        e = e || window.event;
        pos3 = parseInt(e.clientX);
        pos4 = parseInt(e.clientY);
        document.onmouseup = closeDragElement;
        document.onmousemove = elementDrag;
        return false;
    }

    function elementDrag(e) {
        e = e || window.event;
        pos1 = pos3 - parseInt(e.clientX);
        pos2 = pos4 - parseInt(e.clientY);
        pos3 = parseInt(e.clientX);
        pos4 = parseInt(e.clientY);
        elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
        elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
                elmnt.classList.add("drag");
    }

    function closeDragElement() {
        document.onmouseup = null;
        document.onmousemove = null;
                elmnt.classList.remove("drag");
    }
}

Upvotes: 2

Views: 306

Answers (1)

Cesare Polonara
Cesare Polonara

Reputation: 3860

This is a cool task to implement, I played a bit and came up with something like this, but for some reason it's a big buggy, my idea was to use the API : document.elementFromPoint() to get the intersecting element during the drag. It seems to work but it's a bit clumsy and sometimes it fails. Probably it would work better with an IntersectionObserver, that could also make sure to handle multiple boundary drag ( for example if you have 3+ objects, object one will push object 2 that will push object 3, object 3 has to react ).

const draggableElements = document.querySelectorAll(".team_member");
draggableElements.forEach((el, i) => makeDraggable(el, i))

function makeDraggable(elem, idx) {
  elem.onmousedown = dragMouseDown;
  elem.position = {
    x: 0,
    y: 0,
    prevX: 0,
    prevY: 0
  }
  elem.style.left = `calc(35% + ${(idx* 60) + "px"})`
}
// ----- //

function dragMouseDown(e) {
  e = e || window.event;
  this.position.prevX = +(e.clientX);
  this.position.prevY = +(e.clientY);
  document.onmouseup = closeDragElement;
  document.onmousemove = (e) => elementDrag(e, this);
  return false;
}

function elementDrag(e, t) {
  t.position.x = t.position.prevX - +(e.clientX);
  t.position.y = t.position.prevY - +(e.clientY);
  t.position.prevX = +(e.clientX);
  t.position.prevY = +(e.clientY);
  t.style.top = (t.offsetTop - t.position.y) + "px";
  t.style.left = (t.offsetLeft - t.position.x) + "px";
  t.classList.add("drag");
  // Check intersection
  const width = t.getBoundingClientRect().width
  const height = t.getBoundingClientRect().height

  const intersectionElemTop = document.elementFromPoint(e.clientX, e.clientY - height / 2)
  const intersectionElemRight = document.elementFromPoint(e.clientX + width / 2, e.clientY)
  const intersectionElemBottom = document.elementFromPoint(e.clientX, e.clientY + height / 2)
  const intersectionElemLeft = document.elementFromPoint(e.clientX - width / 2, e.clientY)
  const intersections = [{
    type: "top",
    node: intersectionElemTop
  }, {
    type: "right",
    node: intersectionElemRight
  }, {
    type: "bottom",
    node: intersectionElemBottom
  }, {
    type: "left",
    node: intersectionElemLeft
  }].filter(i => [...draggableElements].some(d => i.node === d && d !== t))

  if (intersections[0]) {
    const i = intersections[0]
    switch (i.type) {
      case "top":
        {
          i.node.style.top = t.getBoundingClientRect().y - height + "px";
        }
        break;
      case "right":
        {
          i.node.style.left = t.getBoundingClientRect().x + width + "px";
        }
        break;
      case "bottom":
        {
          i.node.style.top = t.getBoundingClientRect().y + height + "px";
        }
        break;
      case "left":
        {
          i.node.style.left = t.getBoundingClientRect().x - width + "px";
        }
        break;

    }
    i.node.classList.add("dragged");
    clearTimeout(i.timeout)
    i.timeout = setTimeout(() => i.node.classList.remove("dragged"), 500);

  }
}

function closeDragElement(e) {
  document.onmouseup = null;
  document.onmousemove = null;
  e.target.classList.remove("drag");
}
.team_member {
  position: fixed;
  width: 50px;
  height: 50px;
  border: 2px solid red;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: grab;
  top: calc(50% - 25px);
}

.drag {
  background: blue;
}

.dragged {
  background: red;
}
<div class="team_member">
  One
</div>
<div class="team_member">
  Two
</div>

Upvotes: 1

Related Questions