Home > Back-end >  Javascript (vanilla) drag and drop does not work the second time an element is dragged and dropped
Javascript (vanilla) drag and drop does not work the second time an element is dragged and dropped

Time:12-03

I am trying to implement a drag and drop functionality using vanilla Javascript on my web app, where element gets moved to a new position within a div, once it's dragged and dropped.

But I am having an issue where I can drop the element fine the first time, but I can no longer do it the second time onwards.

After debugging, I have noticed that the first time I drop an element, it does not have any inline style (see Appendix A). But when I try and do it the second time, it now has an inline style (see Appendix B) and for some reason, I cannot chnage the values of it. That was also the case after I manually added inline style to my draggable element- I could not drop the item even the first time when I did it.

I am completely out of ideas as to what I could be doing wrong and no similar questions yielded a solution.

Thank you very much in advance for your time.

Code (Unnecessary parts ommitted)

const list = document.querySelector("#list");
const rect = list.getBoundingClientRect();
let oldLeft, oldTop, mouseXStart, mouseYStart;

function dragStart(event) {
  event.dataTransfer.setData("plain/text", event.target.id);
  const item = document.querySelector("#"   event.target.id);

  mouseXStart = event.clientX - rect.left;
  mouseYStart = event.clientY - rect.top;
  oldLeft = item.style.left;
  oldTop = item.style.top;

  console.log(item);
}

function dragOver(event) {
  event.preventDefault();
  event.dataTransfer.dropEffect = "move";
}

function dropItem(event) {
  event.preventDefault();
  const mouseXEnd = event.clientX - rect.left;
  const mouseYEnd = event.clientY - rect.top;
  //Calculate by how much mouse has been moved
  const newLeft = mouseXEnd - mouseXStart;
  const newTop = mouseYEnd - mouseYStart;
  const item = document.querySelector('#'   event.dataTransfer.getData("plain/text"));

  item.style.left = oldLeft   newLeft   "px";
  item.style.top = oldTop   newTop   "px";
}
#list {
  position: relative;
  top: 60px;
  height: 600px;
  width: 100%;
  border: 2px solid rgb(107, 14, 14);
  display: block;
}

#list>div {
  position: absolute;
  height: 200px;
  width: 200px;
  background-color: blue;
  margin: 10px;
  overflow: hidden;
  text-align: left;
}
<div id="list" ondrop="dropItem(event)" ondragover="dragOver(event)">
  <div id="test" draggable="true" ondragstart="dragStart(event)">
    <button type="button"></button>
    <div>
      <p>Test</p>
    </div>
  </div>
</div>

Appendix A

console.log() output on the first dragStart() call:

<div id="test" draggable="true" ondragstart="dragStart(event)">

Appendix B

console.log() output on the second dragStart() call:

<div id="test" draggable="true" ondragstart="dragStart(event)" style="left: 853px; top: 147px;">

CodePudding user response:

Problem

This code

oldLeft   newLeft   "px"

evaluated to something like

123px40px

because

oldLeft = item.style.left

returned string with px at the end

Solution

Parse the value to float

oldLeft = item.style.left ? parseFloat(item.style.left) : 0;
oldTop = item.style.top ? parseFloat(item.style.top) : 0;

const list = document.querySelector("#list");
const rect = list.getBoundingClientRect();
let oldLeft, oldTop, mouseXStart, mouseYStart;

function dragStart(event) {
  event.dataTransfer.setData("plain/text", event.target.id);
  const item = document.querySelector("#"   event.target.id);

  mouseXStart = event.clientX - rect.left;
  mouseYStart = event.clientY - rect.top;
  oldLeft = item.style.left ? parseFloat(item.style.left) : 0;
  oldTop = item.style.top ? parseFloat(item.style.top) : 0;
}

function dragOver(event) {
  event.preventDefault();
  event.dataTransfer.dropEffect = "move";
}

function dropItem(event) {
  event.preventDefault();
  const mouseXEnd = event.clientX - rect.left;
  const mouseYEnd = event.clientY - rect.top;
  //Calculate by how much mouse has been moved
  const newLeft = mouseXEnd - mouseXStart;
  const newTop = mouseYEnd - mouseYStart;
  const item = document.querySelector('#'   event.dataTransfer.getData("plain/text"));

  item.style.left = oldLeft   newLeft   "px";
  item.style.top = oldTop   newTop   "px";
}
#list {
  position: relative;
  top: 60px;
  height: 600px;
  width: 100%;
  border: 2px solid rgb(107, 14, 14);
  display: block;
}

#list>div {
  position: absolute;
  height: 200px;
  width: 200px;
  background-color: blue;
  margin: 10px;
  overflow: hidden;
  text-align: left;
}
<div id="list" ondrop="dropItem(event)" ondragover="dragOver(event)">
  <div id="test" draggable="true" ondragstart="dragStart(event)">
    <button type="button"></button>
    <div>
      <p>Test</p>
    </div>
  </div>
</div>

  • Related