JavaScript How to Dynamically Move Div by Clicking and Dragging

JavascriptHtmlOnmousedown

Javascript Problem Overview


Okay it would seem like it should be simple. I need to take an already existing div and move it according to mouse position within the window. I have searched everywhere and it has led me to over-complicated ways of doing the same thing and involves the use of j-query. I need to strictly use javascript for what I am trying to do.

Method :

var mousePosition;
var div;

(function createDiv(){

    div = document.createElement("div");
    div.style.position = "absolute";
    div.style.left = "0px";
    div.style.top = "0px";
    div.style.width = "100px";
    div.style.height = "100px";
    div.style.background = "red";
    div.style.color = "blue";

    div.addEventListener('mousedown', handleKeyPressed, true);

    document.body.appendChild(div);


})();

function handleKeyPressed(event) {
    
    event.preventDefault();
    
    mousePosition = {
        
        x : event.clientX,
        y : event.clientY
        
    };
    
    div.style.left = mousePosition.x;
    div.style.top = mousePosition.y;
    
    //alert("whoa!");
    
}

Javascript Solutions


Solution 1 - Javascript

I think you're looking for something more like this

var mousePosition;
var offset = [0,0];
var div;
var isDown = false;

div = document.createElement("div");
div.style.position = "absolute";
div.style.left = "0px";
div.style.top = "0px";
div.style.width = "100px";
div.style.height = "100px";
div.style.background = "red";
div.style.color = "blue";

document.body.appendChild(div);

div.addEventListener('mousedown', function(e) {
    isDown = true;
    offset = [
        div.offsetLeft - e.clientX,
        div.offsetTop - e.clientY
    ];
}, true);

document.addEventListener('mouseup', function() {
    isDown = false;
}, true);

document.addEventListener('mousemove', function(event) {
    event.preventDefault();
    if (isDown) {
        mousePosition = {
    
            x : event.clientX,
            y : event.clientY
    
        };
        div.style.left = (mousePosition.x + offset[0]) + 'px';
        div.style.top  = (mousePosition.y + offset[1]) + 'px';
    }
}, true);

FIDDLE

Solution 2 - Javascript

Support for touch inputs

All other answers (including the accepted one) do not work with touch inputs. Touch inputs have events different than that of mouse inputs. See Using Touch Events on MDN.

The following code snippet works even with touch inputs. I have highlighted all lines of code that need to be added to support touch devices.
The basic idea here is that every element containing draggable in the class list should be draggable. This concept is easier to follow when you have a big number of elements that need to be dragged.

See this Glitch page and following for a demo.

const d = document.getElementsByClassName("draggable");

for (let i = 0; i < d.length; i++) {
  d[i].style.position = "relative";
}

function filter(e) {
  let target = e.target;

  if (!target.classList.contains("draggable")) {
    return;
  }

  target.moving = true;

  //NOTICE THIS 👇 Check if Mouse events exist on users' device
  if (e.clientX) {
    target.oldX = e.clientX; // If they exist then use Mouse input
    target.oldY = e.clientY;
  } else {
    target.oldX = e.touches[0].clientX; // Otherwise use touch input
    target.oldY = e.touches[0].clientY;
  }
  //NOTICE THIS 👆 Since there can be multiple touches, you need to mention which touch to look for, we are using the first touch only in this case

  target.oldLeft = window.getComputedStyle(target).getPropertyValue('left').split('px')[0] * 1;
  target.oldTop = window.getComputedStyle(target).getPropertyValue('top').split('px')[0] * 1;

  document.onmousemove = dr;
  //NOTICE THIS 👇
  document.ontouchmove = dr;
  //NOTICE THIS 👆

  function dr(event) {
    event.preventDefault();

    if (!target.moving) {
      return;
    }
    //NOTICE THIS 👇
    if (event.clientX) {
      target.distX = event.clientX - target.oldX;
      target.distY = event.clientY - target.oldY;
    } else {
      target.distX = event.touches[0].clientX - target.oldX;
      target.distY = event.touches[0].clientY - target.oldY;
    }
    //NOTICE THIS 👆

    target.style.left = target.oldLeft + target.distX + "px";
    target.style.top = target.oldTop + target.distY + "px";
  }

  function endDrag() {
    target.moving = false;
  }
  target.onmouseup = endDrag;
  //NOTICE THIS 👇
  target.ontouchend = endDrag;
  //NOTICE THIS 👆
}
document.onmousedown = filter;
//NOTICE THIS 👇
document.ontouchstart = filter;
//NOTICE THIS 👆

.draggable {
  width: 100px;
  height: 100px;
  background: red;
}

<div class="draggable"></div>

Solution 3 - Javascript

Check if this is smoother than adeneo: FIDDLE

var m = document.getElementById('move');
m.addEventListener('mousedown', mouseDown, false);
window.addEventListener('mouseup', mouseUp, false);

function mouseUp() {
    window.removeEventListener('mousemove', move, true);
}

function mouseDown(e) {
    window.addEventListener('mousemove', move, true);
}

function move(e) {
    m.style.top = e.clientY + 'px';
    m.style.left = e.clientX + 'px';
};

Solution 4 - Javascript

I just made a small change to the @adeneo very well working answer. If everything is enclosed in a function, and every event is attached to the div, you can use it as part of a library.

Call the following function passing an id. If the div does not exist it is created.

function drag_div(div_id){
var div;

div = document.getElementById(div_id);

if(div == null){
   div = document.createElement("div");
   div.id = div_id;
   div.style.position = "absolute";
   div.style.left = "0px";
   div.style.top = "0px";
   div.style.width = "100px";
   div.style.height = "100px";
   div.style.background = "red";
   div.style.color = "blue";
   document.body.appendChild(div);
}

div.addEventListener('mousedown', function(e) {
    div.isDown = true;
    div.offset = [
        div.offsetLeft - e.clientX,
        div.offsetTop - e.clientY
    ];
}, true);

div.addEventListener('mouseup', function() {
    div.isDown = false;
}, true);

div.addEventListener('mousemove', function(event) {
    event.preventDefault();
    if (div.isDown) {
        div.mousePosition = {

            x : event.clientX,
            y : event.clientY

        };
        div.style.left = (div.mousePosition.x + div.offset[0]) + 'px';
        div.style.top  = (div.mousePosition.y + div.offset[1]) + 'px';
    }
}, true);
}

Solution 5 - Javascript

jquery is much easier to deploy. I am surprised you say you don't want to learn it.

You can save the jquery file in your local computer so you do not need internet to use jquery features.

In my case i have saved it in tools folder. So i do not need to be on internet.

For all the js many lines of js code answered above you only need one small line.

 <script src="/common/tools/jquery-1.10.2.js"></script>
 <script src="/common/tools/jquery-ui.js"></script>

 <script>
   $(function() {
   $( "#mydiv_to_make_draggable" ).draggable();
   });
</script>

Solution 6 - Javascript

You can use this one as a library. Works perfectly. I found it on github but it was getting stuck sometimes because the sharer put "mouseup" to element. I changed it to document and it fixed the problem. This is fixed version

'use strict';

/**
 * Makes an element draggable.
 *
 * @param {HTMLElement} element - The element.
 */
function draggable(element) {
	var isMouseDown = false;

    // initial mouse X and Y for `mousedown`
    var mouseX;
    var mouseY;

    // element X and Y before and after move
    var elementX = 0;
    var elementY = 0;

	// mouse button down over the element
    element.addEventListener('mousedown', onMouseDown);

	/**
     * Listens to `mousedown` event.
     *
     * @param {Object} event - The event.
     */
	function onMouseDown(event) {
        mouseX = event.clientX;
        mouseY = event.clientY;
        isMouseDown = true;
    }

	// mouse button released
    document.addEventListener('mouseup', onMouseUp);

	/**
     * Listens to `mouseup` event.
     *
     * @param {Object} event - The event.
     */
	function onMouseUp(event) {
        isMouseDown = false;
        elementX = parseInt(element.style.left) || 0;
        elementY = parseInt(element.style.top) || 0;
    }

	// need to attach to the entire document
    // in order to take full width and height
    // this ensures the element keeps up with the mouse
    document.addEventListener('mousemove', onMouseMove);

	/**
     * Listens to `mousemove` event.
     *
     * @param {Object} event - The event.
     */
	function onMouseMove(event) {
    	if (!isMouseDown) return;
        var deltaX = event.clientX - mouseX;
        var deltaY = event.clientY - mouseY;
        element.style.left = elementX + deltaX + 'px';
        element.style.top = elementY + deltaY + 'px';
    }
}

Solution 7 - Javascript

Here's another approach that includes touch input.

dragElement(document.getElementById('mydiv'));

function dragElement(element) {
    var startX = 0, startY = 0, endX = 0, endY = 0;
    element.onmousedown = dragStart;
    element.ontouchstart = dragStart;

    function dragStart(e) {
        e = e || window.event;
        e.preventDefault();
        // mouse cursor position at start  
        if (e.clientX) {  // mousemove
            startX = e.clientX;
            startY = e.clientY;
        } else { // touchmove - assuming a single touchpoint
            startX = e.touches[0].clientX
            startY = e.touches[0].clientY
        }
        document.onmouseup = dragStop;
        document.ontouchend = dragStop;
        document.onmousemove = elementDrag;  // call whenever the cursor moves
        document.ontouchmove = elementDrag;
    }

    function elementDrag(e) {
        e = e || window.event;
        e.preventDefault();
        // calculate new cursor position
        if (e.clientX) {
            endX = startX - e.clientX;
            endY = startY - e.clientY;
            startX = e.clientX;
            startY = e.clientY;
        } else {
            endX = startX - e.touches[0].clientX;
            endY = startY - e.touches[0].clientY;
            startX = e.touches[0].clientX;
            startY = e.touches[0].clientY;
        }
        // set the new position
        element.style.left = (element.offsetLeft - endX) + "px";
        element.style.top = (element.offsetTop - endY) + "px";
    }

    function dragStop() {
        // stop moving on touch end / mouse btn is released 
        document.onmouseup = null;
        document.onmousemove = null;
        document.ontouchend = null;
        document.ontouchmove = null;
    }
}

Solution 8 - Javascript

Accepted answer with touch added

The accepted answer from adeneo is really elegant and works well. However it only works for mouse clicks, so here is an amended version which includes touch input:

var position;
var offset = [0,0];
var isDown = false;

function makeDraggable(el){

	['mousedown', 'touchstart'].forEach( evt => 
		el.addEventListener(evt, pickup, true)
	);
	
	['mousemove', 'touchmove'].forEach( evt => 
		el.addEventListener(evt, move, true)
	);

	['mouseup', 'touchend'].forEach( evt => 
		el.addEventListener(evt, drop, true)
	);		
		
	function pickup(e) {
		isDown = true;
		if (e.clientX) {
			offset = [el.offsetLeft - e.clientX, el.offsetTop - e.clientY];
		}
		else if (e.touches) {  
			// for touch devices, use 1st touch only
			offset = [el.offsetLeft - e.touches[0].pageX, el.offsetTop - e.touches[0].pageY];
		}		
	}
	function move(e) {
		if (isDown) {
			if (e.clientX) {
				position = {x : e.clientX, y : e.clientY};
			}
			else if (e.touches) {
				position = {x : e.touches[0].pageX, y : e.touches[0].pageY};			
			}			
			el.style.left = (position.x + offset[0]) + 'px';
			el.style.top  = (position.y + offset[1]) + 'px';
		}
	}
	function drop(e) {
		// seems not to be needed for Android Chrome
		// and modern browsers on Mac & PC
		// but is required for iPad & iPhone
		isDown = false;		
		el.style.left = (position.x + offset[0]) + 'px';
		el.style.top  = (position.y + offset[1]) + 'px';
	}
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionStoneAgeCoderView Question on Stackoverflow
Solution 1 - JavascriptadeneoView Answer on Stackoverflow
Solution 2 - JavascriptmmaismmaView Answer on Stackoverflow
Solution 3 - JavascriptjhyapView Answer on Stackoverflow
Solution 4 - JavascriptminivipView Answer on Stackoverflow
Solution 5 - JavascriptwebzyView Answer on Stackoverflow
Solution 6 - JavascriptKyoko SasagavaView Answer on Stackoverflow
Solution 7 - JavascriptMaxiView Answer on Stackoverflow
Solution 8 - JavascriptBob OsolaView Answer on Stackoverflow