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>