Reputation: 1058
I have just learned the basics of HTML5 draggable and droppable API. I do not know how to retain the dragged position at its dropped position. On dropping it changes its position.
Here is the basic HTML (relevant elements only):
onDragStart = function(ev) {
//...
}
drop_handler = function(ev) {
ev.preventDefault();
ev.target.appendChild(document.getElementById("id1"));
}
dragover_handler = function(ev) {
ev.preventDefault();
ev.dataTransfer.dropEffect = "move";
}
<div id="id1" draggable="true" ondragstart="onDragStart(event)" style="border:2px solid green; cursor:pointer;width:100px;height:50px;">Dragged Div</div>
<div id="id2" style="position:absolute;left:100px;top:200px;border:2px solid red; cursor:pointer;width:200px;height:200px;" ondrop="drop_handler(event)" ondragover="dragover_handler(event)">Drop Div
</div>
I want the dragged element to remain at its final position where it it is dropped. Any pointers will be appreciated.
Upvotes: 6
Views: 15092
Reputation: 7913
As mentioned in my comment, <div id="id1" ...>
still has the default positioning which is static
. Once you append it to the dropDiv, it assumes normal document flow behavior. Because it's a block element, it goes underneath the text that is already there and fills the width of the block.
If you want it to stay exactly where it is when you drop it, you need to give it position: absolute
and take into account how the mouse moved while dragging it on the screen. In the dragStart
event, we capture the original coordinates of the dragDiv and account for where the mouse was inside relative to the top-left corner. When we drop the dragDiv into the dropDiv, we set the absolute positioning and account for how much the mouse moved during the drag.
Since dragDiv is now a child of dropDiv, we need our new top and left values to be relative to dropDiv's coordinates rather than the entire screen, so we subtract out dropDiv's top and left values. Note well that some of these methods may not take into account the borders around the elements which may make it look like it's a pixel or two off -- to fix that you can either subtract a one or two pixels in the calculation or give them box-sizing: border-box
.
let offsetX;
let offsetY;
onDragStart = function(ev) {
const rect = ev.target.getBoundingClientRect();
offsetX = ev.clientX - rect.x;
offsetY = ev.clientY - rect.y;
};
drop_handler = function(ev) {
ev.preventDefault();
const left = parseInt(id2.style.left);
const top = parseInt(id2.style.top);
id1.style.position = 'absolute';
id1.style.left = ev.clientX - left - offsetX + 'px';
id1.style.top = ev.clientY - top - offsetY + 'px';
id2.appendChild(document.getElementById("id1"));
};
dragover_handler = function(ev) {
ev.preventDefault();
ev.dataTransfer.dropEffect = "move";
};
<div id="id1" draggable="true" ondragstart="onDragStart(event)" style="border:2px solid green; cursor:pointer;width:100px;height:50px;">Dragged Div</div>
<div id="id2" style="position:absolute;left:200px;top:50px;border:2px solid red; cursor:pointer;width:200px;height:200px;" ondrop="drop_handler(event)" ondragover="dragover_handler(event)">Drop Div
</div>
Upvotes: 9