11

Im struggling with seemingly a simple javascript exercise, writing a vanilla drag and drop. I think Im making a mistake with my 'addeventlisteners', here is the code:

var ele = document.getElementsByClassName ("target")[0];
var stateMouseDown = false;
//ele.onmousedown = eleMouseDown;
ele.addEventListener ("onmousedown" , eleMouseDown , false);

function eleMouseDown () {
    stateMouseDown = true;
    document.addEventListener ("onmousemove" , eleMouseMove , false);
}

function eleMouseMove (ev) {
    do {
        var pX = ev.pageX;
        var pY = ev.pageY;
        ele.style.left = pX + "px";
        ele.style.top = pY + "px";
        document.addEventListener ("onmouseup" , eleMouseUp , false);
    } while (stateMouseDown === true);
}

function eleMouseUp () {
    stateMouseDown = false;
    document.removeEventListener ("onmousemove" , eleMouseMove , false);
    document.removeEventListener ("onmouseup" , eleMouseUp , false);
}
Kayote
  • 14,579
  • 25
  • 85
  • 144

5 Answers5

12

Here's a jsfiddle with it working: http://jsfiddle.net/fpb7j/1/

There were 2 main issues, first being the use of onmousedown, onmousemove and onmouseup. I believe those are only to be used with attached events:

document.body.attachEvent('onmousemove',drag);

while mousedown, mousemove and mouseup are for event listeners:

document.body.addEventListener('mousemove',drag);

The second issue was the do-while loop in the move event function. That function's being called every time the mouse moves a pixel, so the loop isn't needed:

var ele = document.getElementsByClassName ("target")[0];
ele.addEventListener ("mousedown" , eleMouseDown , false);

function eleMouseDown () {
    stateMouseDown = true;
    document.addEventListener ("mousemove" , eleMouseMove , false);
}

function eleMouseMove (ev) {
    var pX = ev.pageX;
    var pY = ev.pageY;
    ele.style.left = pX + "px";
    ele.style.top = pY + "px";
    document.addEventListener ("mouseup" , eleMouseUp , false);
}

function eleMouseUp () {
    document.removeEventListener ("mousemove" , eleMouseMove , false);
    document.removeEventListener ("mouseup" , eleMouseUp , false);
}

By the way, I had to make the target's position absolute for it to work.

iRector
  • 1,935
  • 4
  • 22
  • 30
  • Thanks for the detailed reply. I was using the Do While for mousemove while mousedown is active, otherwise it should not run... if I enable the Do While loop, Firefox comes to a halt though. – Kayote Aug 25 '13 at 03:45
  • @Kayote I gotcha, though it won't be necessary due to how often the `mousemove` event calls `eleMouseMove()` – iRector Aug 25 '13 at 04:33
  • I updated it a little for my requirement. It can be helpful for others too, thus http://jsfiddle.net/kjwLe9bq/ sharing. – Savaratkar Apr 08 '16 at 02:30
  • This is from long ago, but it's worth noting (as the code in the last comment demonstrates) that the event listener for mouseup should be added in the eleMouseDown function, not in eleMouseMove; the latter is called a _lot_, and you don't want to be adding a mouseup listener every time the user moves their mouse. – Steven Stadnicki Apr 21 '17 at 04:58
  • Version to avoid the jump on the initial mouse move: http://jsfiddle.net/fpb7j/308/ – Zach Saucier Dec 29 '17 at 05:38
12

you can try this fiddle too, http://jsfiddle.net/dennisbot/4AH5Z/

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>titulo de mi pagina</title>
<style>
    #target {
        width: 100px;
        height: 100px;
        background-color: #ffc;
        border: 2px solid blue;
        position: absolute;
    }
</style>
<script>
   window.onload = function() {
        var el = document.getElementById('target');
        var mover = false, x, y, posx, posy, first = true;
        el.onmousedown = function() {
            mover = true;
        };
        el.onmouseup = function() {
            mover = false;
            first = true;
        };
        el.onmousemove = function(e) {
            if (mover) {
                if (first) {
                    x = e.offsetX;
                    y = e.offsetY;
                    first = false;
                }
                posx = e.pageX - x;
                posy = e.pageY - y;
                this.style.left = posx + 'px';
                this.style.top = posy + 'px';
            }
        };
   }
</script>
</head>
<body>
    <div id="target" style="left: 10px; top:20px"></div>
</body>
</html>
dennisbot
  • 950
  • 1
  • 11
  • 22
1

I've just made a simple drag.

It's a one liner usage, and it handles things like the offset of the mouse to the top left corner of the element, onDrag/onStop callbacks, and SVG elements dragging

Here is the code.

// simple drag
function sdrag(onDrag, onStop) {
    var startX = 0;
    var startY = 0;
    var el = this;
    var dragging = false;

    function move(e) {
        el.style.left = (e.pageX - startX ) + 'px';
        el.style.top = (e.pageY - startY ) + 'px';
        onDrag && onDrag(el, e.pageX, startX, e.pageY, startY);
    }

    function startDragging(e) {
        if (e.currentTarget instanceof HTMLElement || e.currentTarget instanceof SVGElement) {
            dragging = true;
            var left = el.style.left ? parseInt(el.style.left) : 0;
            var top = el.style.top ? parseInt(el.style.top) : 0;
            startX = e.pageX - left;
            startY = e.pageY - top;
            window.addEventListener('mousemove', move);
        }
        else {
            throw new Error("Your target must be an html element");
        }
    }

    this.addEventListener('mousedown', startDragging);
    window.addEventListener('mouseup', function (e) {
        if (true === dragging) {
            dragging = false;
            window.removeEventListener('mousemove', move);
            onStop && onStop(el, e.pageX, startX, e.pageY, startY);
        }
    });
}

Element.prototype.sdrag = sdrag;

and to use it:

document.getElementById('my_target').sdrag();

You can also use onDrag and onStop callbacks:

document.getElementById('my_target').sdrag(onDrag, onStop);

Check my repo here for more details: https://github.com/lingtalfi/simpledrag

ling
  • 9,545
  • 4
  • 52
  • 49
1

this is how I do it

var MOVE = {
  startX: undefined,
  startY: undefined,
  item: null
};

function contentDiv(color, width, height) {
  var result = document.createElement('div');
  result.style.width = width + 'px';
  result.style.height = height + 'px';
  result.style.backgroundColor = color;
  return result;
}

function movable(content) {
  var outer = document.createElement('div');
  var inner = document.createElement('div');
  outer.style.position = 'relative';
  inner.style.position = 'relative';
  inner.style.cursor = 'move';
  inner.style.zIndex = 1000;
  outer.appendChild(inner);
  inner.appendChild(content);
  inner.addEventListener('mousedown', function(evt) {
    MOVE.item = this;
    MOVE.startX = evt.pageX;
    MOVE.startY = evt.pageY;
  })
  return outer;
}

function bodyOnload() {
  document.getElementById('td1').appendChild(movable(contentDiv('blue', 100, 100)));
  document.getElementById('td2').appendChild(movable(contentDiv('red', 100, 100)));
  document.addEventListener('mousemove', function(evt) {
    if (!MOVE.item) return;
    if (evt.which!==1){ return; }
    var dx = evt.pageX - MOVE.startX;
    var dy = evt.pageY - MOVE.startY;
    MOVE.item.parentElement.style.left = dx + 'px';
    MOVE.item.parentElement.style.top = dy + 'px';
  });
  document.addEventListener('mouseup', function(evt) {
    if (!MOVE.item) return;
    var dx = evt.pageX - MOVE.startX;
    var dy = evt.pageY - MOVE.startY;
    var sty = MOVE.item.style;
    sty.left = (parseFloat(sty.left) || 0) + dx + 'px';
    sty.top = (parseFloat(sty.top) || 0) + dy + 'px';
    MOVE.item.parentElement.style.left = '';
    MOVE.item.parentElement.style.top = '';
    MOVE.item = null;
    MOVE.startX = undefined;
    MOVE.startY = undefined;
  });
}
bodyOnload();
table {
user-select: none
}
<table>
  <tr>
    <td id='td1'></td>
    <td id='td2'></td>
  </tr>
</table>

While dragging, the left and right of the style of the parentElement of the dragged element are continuously updated. Then, on mouseup (='drop'), "the changes are committed", so to speak; we add the (horizontal and vertical) position changes (i.e., left and top) of the parent to the position of the element itself, and we clear left/top of the parent again. This way, we only need JavaScript variables for pageX, pageY (mouse position at drag start), while concerning the element position at drag start, we don't need JavaScript variables for that (just keeping that information in the DOM).

If you're dealing with SVG elements, you can use the same parent/child/commit technique. Just use two nested g, and use transform=translate(dx,dy) instead of style.left=dx, style.top=dy

mathheadinclouds
  • 3,507
  • 2
  • 27
  • 37
-2

https://docs.oracle.com/javase/8/javafx/events-tutorial/hellodraganddropjava.htm#CHDEAEAI

Simple way using dragboard and ClipBoard