Search code examples
javascripthtmldrag-and-drophtml5-draggable

HTML5 drag and drop, dropped div moves a bit from dropped position


I have the following working code (also see this fiddle):

<!DOCTYPE HTML>
<html>
<head>
<style>
#div1 {width:350px;height:170px;padding:10px;border:1px solid #aaaaaa;}
#drag1 {width:50px;height:50px;padding:10px;border:1px solid #000;background-color: #f00; position: absolute;}
</style>
<script>
function allowDrop(ev) {
    ev.preventDefault();
}

function drag(ev) {
    ev.dataTransfer.setData("text/html", ev.target.id);
}

function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text/html");
ev.target.appendChild(document.getElementById(data));
//window.alert( ev.clientX + ',' + ev.clientY);
document.getElementById("drag1").style.left = ev.clientX + 'px';
document.getElementById("drag1").style.top = ev.clientY + 'px';
return false;

}

<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)">
    <div id="drag1" draggable="true" ondragstart="drag(event)"></div>
</div>
</body>
</html>

Here you can drag and drop a colored div only inside the main div. The problem is that this red div jumps a bit from his dropped position. I think this is because I use the mouse ev.clientX and Y. So how can I fix this so the DIV stays at the dropped position?

    function allowDrop(ev) {
        ev.preventDefault();
    }
    
    function drag(ev) {
        ev.dataTransfer.setData("text/html", ev.target.id);
    }
    
    function drop(ev) {
        ev.preventDefault();
        var data = ev.dataTransfer.getData("text/html");
        ev.target.appendChild(document.getElementById(data));
         //window.alert( ev.clientX + ',' + ev.clientY);
        document.getElementById("drag1").style.left = ev.clientX + 'px';
        document.getElementById("drag1").style.top = ev.clientY + 'px';
        return false;
    }
    #div1 {width:350px;height:170px;padding:10px;border:1px solid #aaaaaa;}
    #drag1 {width:50px;height:50px;padding:10px;border:1px solid #000;background-color: #f00; position: absolute;}
    
    
    <div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)">
        <div id="drag1" draggable="true" ondragstart="drag(event)"></div>
    </div>


Solution

  • You're dropping the div's top left corner to the mouse's location, you have to find the offset from inside the div where the mouse is actually dragging. By checking the event object(in chrome), offsetX and offsetY and layerX and layerY seem to have the offset from the origin of the div to the mouse.

    Here is a question with alot of discussion on offsetXY and layerXY and associated properties event.offsetX in Firefox, note: don't just read the accepted answer.

    function allowDrop(ev) {
        ev.preventDefault();
    }
    
    function drag(ev) {
        ev.dataTransfer.setData("application/json", 
        JSON.stringify([ev.target.id, 
            (ev.offsetX || ev.clientX - $(ev.target).offset().left),
            (ev.offsetY || ev.clientY - $(ev.target).offset().top)]
        ));
    }
        
    function drop(ev) {
        ev.preventDefault();
        var data = JSON.parse(ev.dataTransfer.getData("application/json"));
        ev.target.appendChild(document.getElementById(data[0]));
        //window.alert( ev.clientX + ',' + ev.clientY);
        document.getElementById("drag1")
            .style.left = ev.clientX - data[1] + 'px';
            document.getElementById("drag1").style.top = ev.clientY - data[2] + 'px';
        return false;
    }
    #div1 { width:350px; height:170px; padding:10px; border:1px solid #aaaaaa; }
    #drag1 { width:50px; height:50px; padding:10px; border:1px solid #000; background-color: #f00; position: absolute; }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    
    <div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)">
        <div id="drag1" draggable="true" ondragstart="drag(event)"></div>
    </div>