Create a draggable div in native javascript

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


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>

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.

Tags:

Javascript