Create a draggable div in native javascript [duplicate]
Asked Answered
C

4

14

I want to create a movable/draggable div in native javascript without using jquery and libraries. Is there a tutorial or anythign?

Corpus answered 31/10, 2012 at 6:43 Comment(2)
Take a look at here [#9334584Iolanthe
To give you a basic idea of how it works: first you need to attach onmousedown and onmouseup event handlers to a div. Then you need to change the div's x and y coordinates to those of the pointer position (but you have to take into account the offset at which the element was first dragged). Oh, and don't forget to set the element's position to "absolute"Reade
M
16

OK, here's my personal code that I use for lightweight deployments (projects where using a library is either not allowed or overkill for some reason). First thing first, I always use this convenience function so that I can pass either an id or the actual dom element:

function get (el) {
  if (typeof el == 'string') return document.getElementById(el);
  return el;
}

As a bonus, get() is shorter to type than document.getElementById() and my code ends up shorter.

Second realize that what most libraries are doing is cross-browser compatibility. If all browsers behave the same the code is fairly trivial. So lets write some cross-browser functions to get mouse position:

function mouseX (e) {
  if (e.pageX) {
    return e.pageX;
  }
  if (e.clientX) {
    return e.clientX + (document.documentElement.scrollLeft ?
      document.documentElement.scrollLeft :
      document.body.scrollLeft);
  }
  return null;
}

function mouseY (e) {
  if (e.pageY) {
    return e.pageY;
  }
  if (e.clientY) {
    return e.clientY + (document.documentElement.scrollTop ?
      document.documentElement.scrollTop :
      document.body.scrollTop);
  }
  return null;
}

OK, the two functions above are identical. There're certainly better ways to write them but I'm keeping it (relatively) simple for now.

Now we can write the drag and drop code. The thing I like about this code is that everything's captured in a single closure so there are no global variables or helper functions littering the browser. Also, the code separates the drag handle from the object being dragged. This is useful for creating dialog boxes etc. But if not needed, you can always assign them the same object. Anyway, here's the code:

function dragable (clickEl,dragEl) {
  var p = get(clickEl);
  var t = get(dragEl);
  var drag = false;
  offsetX = 0;
  offsetY = 0;
  var mousemoveTemp = null;

  if (t) {
    var move = function (x,y) {
      t.style.left = (parseInt(t.style.left)+x) + "px";
      t.style.top  = (parseInt(t.style.top) +y) + "px";
    }
    var mouseMoveHandler = function (e) {
      e = e || window.event;

      if(!drag){return true};

      var x = mouseX(e);
      var y = mouseY(e);
      if (x != offsetX || y != offsetY) {
        move(x-offsetX,y-offsetY);
        offsetX = x;
        offsetY = y;
      }
      return false;
    }
    var start_drag = function (e) {
      e = e || window.event;

      offsetX=mouseX(e);
      offsetY=mouseY(e);
      drag=true; // basically we're using this to detect dragging

      // save any previous mousemove event handler:
      if (document.body.onmousemove) {
        mousemoveTemp = document.body.onmousemove;
      }
      document.body.onmousemove = mouseMoveHandler;
      return false;
    }
    var stop_drag = function () {
      drag=false;      

      // restore previous mousemove event handler if necessary:
      if (mousemoveTemp) {
        document.body.onmousemove = mousemoveTemp;
        mousemoveTemp = null;
      }
      return false;
    }
    p.onmousedown = start_drag;
    p.onmouseup = stop_drag;
  }
}

There is a reason for the slightly convoluted offsetX/offsetY calculations. If you notice, it's just taking the difference between mouse positions and adding them back to the position of the div being dragged. Why not just use the mouse positions? Well, if you do that the div will jump to the mouse pointer when you click on it. Which is a behavior I did not want.

Messuage answered 31/10, 2012 at 8:14 Comment(3)
That code above is the demo. Copy paste it into an html file (including the helper functions) and call the function on a div in the page.Messuage
NOTE: I would also put p.onmouseout = stop_drag; near the end with the mouse events. When I was running the code sometimes my mouse would move faster than the element and when the mouse gets off of the element you have to first click and release on it before it'll stop acting up.Abraham
To run it just put this in your script: window.onload=function(){dragable('testdiv','testdiv');}; } and this in your HTML: <div id="testdiv" style="background-color: lightgreen; position:absolute; top:300px; left: 300px;">H train</div>Abraham
E
14

You can try this

HTML

<div id="one" style="height:50px; width:50px; border:1px solid #ccc; background:red;">
</div>

Js Script for draggable div

window.onload = function(){
    draggable('one');
};

var dragObj = null;
function draggable(id)
{
    var obj = document.getElementById(id);
    obj.style.position = "absolute";
    obj.onmousedown = function(){
            dragObj = obj;
    }
}

document.onmouseup = function(e){
    dragObj = null;
};

document.onmousemove = function(e){
    var x = e.pageX;
    var y = e.pageY;

    if(dragObj == null)
        return;

    dragObj.style.left = x +"px";
    dragObj.style.top= y +"px";
};

Check this Demo

Expiable answered 31/10, 2012 at 6:55 Comment(4)
How do I keep it from moving the mouse to top left corner. I'd like to leave the mouse where it was dropped relative to where it was clicked in the divPhyliciaphylis
This is awesome and simple, I just added one tiny change. In order to prevent leaks if obj is removed from the dom, the obj.onmousedown handler should not reference obj, it should reference e.currentTarget.Bonita
Best solution. Thanks.Anet
This would work for simple situations, but as soon as you have nested elements it won't work. And if you have any items after the draggable your layout will break due to the hard-set of position: absolute.Emmi
S
13

This code corrects the position of the mouse (so the dragged object doesn't jump when you start dragging) and works with touch screens/phones as well

var dragObj = null; //object to be moved
var xOffset = 0; //used to prevent dragged object jumping to mouse location
var yOffset = 0;
	
window.onload = function()
{
	document.getElementById("menuBar").addEventListener("mousedown", startDrag, true);
	document.getElementById("menuBar").addEventListener("touchstart", startDrag, true);
	document.onmouseup = stopDrag;
	document.ontouchend = stopDrag;
}

function startDrag(e)
/*sets offset parameters and starts listening for mouse-move*/
{
	e.preventDefault();
	e.stopPropagation();
	dragObj = e.target;
	dragObj.style.position = "absolute";
	var rect = dragObj.getBoundingClientRect();
	
	if(e.type=="mousedown")
	{
		xOffset = e.clientX - rect.left; //clientX and getBoundingClientRect() both use viewable area adjusted when scrolling aka 'viewport'
		yOffset = e.clientY - rect.top;
		window.addEventListener('mousemove', dragObject, true);
	}
	else if(e.type=="touchstart")
	{
		xOffset = e.targetTouches[0].clientX - rect.left; //clientX and getBoundingClientRect() both use viewable area adjusted when scrolling aka 'viewport'
		yOffset = e.targetTouches[0].clientY - rect.top;
		window.addEventListener('touchmove', dragObject, true);
	}
}

function dragObject(e)
/*Drag object*/
{
	e.preventDefault();
	e.stopPropagation();
	
	if(dragObj == null) return; // if there is no object being dragged then do nothing
    else if(e.type=="mousemove")
	{
		dragObj.style.left = e.clientX-xOffset +"px"; // adjust location of dragged object so doesn't jump to mouse position
		dragObj.style.top = e.clientY-yOffset +"px";
	}
    else if(e.type=="touchmove")
	{
		dragObj.style.left = e.targetTouches[0].clientX-xOffset +"px"; // adjust location of dragged object so doesn't jump to mouse position
		dragObj.style.top = e.targetTouches[0].clientY-yOffset +"px";
	}
}

function stopDrag(e)
/*End dragging*/
{
	if(dragObj) 
	{
		dragObj = null;
		window.removeEventListener('mousemove', dragObject, true);
		window.removeEventListener('touchmove', dragObject, true);
	}
}
div{height:400px; width:400px; border:1px solid #ccc; background:blue; cursor: pointer;}
<div id="menuBar" >A</div>
Solorzano answered 17/1, 2017 at 13:33 Comment(1)
I updated your code to subscribe to the touchend event as well. Otherwise dragging won't stop on touch devices.Renaissance
G
-3

div {margin:1rem; border:1px solid; padding:0.5rem}
<div
  draggable=true 
  ondragstart="event.dataTransfer.setData('text/plain', '12345')"
>
drag me
</div>
    
<div 
  ondragover="return false;" 
  ondrop="this.innerHTML = event.dataTransfer.getData('text/plain')"
>
drop on me
</div>
Guienne answered 31/10, 2012 at 7:7 Comment(1)
Cool, but probably not what OP meant by a "draggable div"...Authorization

© 2022 - 2024 — McMap. All rights reserved.