How to spoof dragging all elements of the same class with javascript? - javascript

I'm trying to make it so that a bunch of elements will move when the user holds down their mouse button and then drags the mouse. I'm trying to do this by listening to the mousedown and mousemove events and comparing their locations. The difference in the locations of is used to calculate how far to move all of the elements. The goal is that it will feel like all of the elements have been dragged. For some reason though, my approach results in the elements flying all over the place.
Here's an example of what I'm trying to do:
window.onload = function(){
var mouse = {isDown : false, lastX : null, lastY : null };
document.body.addEventListener("click", function(event){
var el = document.createElement("div");
el.className = "box";
el.style.left = event.clientX + "px";
el.style.top = event.clientY + "px";
el.style.backgroundColor = "BLACK";
el.style.width = "16px";
el.style.height = "16px";
el.style.position = "absolute";
document.body.appendChild(el);
});
document.body.addEventListener("mousedown", function(event){
mouse.isDown = true;
mouse.lastX = event.clientX;
mouse.lastY = event.clientY;
});
document.body.addEventListener("mouseup", function(){
mouse.isDown = false;
mouse.lastX = null;
mouse.lastY = null;
});
var removePx = function(string){return string.substring(0, string.length -2);}
document.body.addEventListener("mousemove", function(event){
console.log("move (" + event.clientX + "," + event.clientY + ")");
if(mouse.isDown){
var deltaX = event.clientX - mouse.lastX;
var deltaY = event.clientY - mouse.lastY;
console.log("DeltaX " + deltaX);
console.log("DeltaY " + deltaY);
var boxes = document.getElementsByClassName("box");
for(var i = 0; i<boxes.length; i++){
var box = boxes[i];
box.style.left = removePx(box.style.left) + deltaX + "px";
box.style.top = removePx(box.style.top) + deltaY + "px";
}
mouse.lastX = event.clientX;
mouse.lastY = event.clientY;
}
});
}
What's going wrong here? Also, is there a better way to accomplish this?

Regarding your code, i'd say that transform is preferrable regarding positioning for this kind of thing (instead of left/top) - see the discussion http://www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/.
Anyway, i didn't take a closer look at your code, but instead, tried a solution myself to what i understood of it. I just used a simple pattern regarding drag and drop which is:
onmousedown - setState to moving, store the clicked point, get the current matrix of the transformation applied
onmousemove - calculate dx, dy, accumulate this differences in the matrix, update current x and y in the state
onmouseup - just remove the moving state
Here's a snippet:
var main = document.querySelector('main');
var movable = [].slice.call(document.querySelectorAll('.movable'));
var _state = {
x: 0,
y: 0,
matrix: [1, 0, 0, 1, 0, 0],
moving: false
};
movable.forEach(function (elem) {
elem.style.transform = 'matrix(' + _state.matrix.join(',') + ')'
});
function onMouseDown (e) {
_state.moving = true;
_state.x = e.clientX;
_state.y = e.clientY;
_state.matrix = matrixToArray(e.target.style.transform);
}
function matrixToArray (matrix) {
return matrix
.match(/matrix\((.*)\)/)[1]
.split(' ')
.join('')
.split(',')
.map(function (a) {
return +a;
});
}
function onMouseMove (e) {
if (!_state.moving)
return;
var dx = e.clientX - _state.x;
var dy = e.clientY - _state.y;
_state.matrix[4] += dx;
_state.matrix[5] += dy;
var transf = 'matrix(' + _state.matrix.join(',') + ')'
movable.forEach(function (elem) {
elem.style.transform = transf;
});
_state.x = e.clientX;
_state.y = e.clientY;
}
function onMouseUp (e) {
_state.moving = false;
_state.x = e.clientX;
_state.y = e.clientY;
}
main.addEventListener('mousemove', onMouseMove);
main.addEventListener('mouseup', onMouseUp);
movable.forEach(function (elem) {
elem.addEventListener('mousedown', onMouseDown);
});
html,
body,
main {
width: 100%;
height: 100%;
}
div {
width: 50px;
height: 50px;
position: absolute;
}
#d1 { background: black; left: 10px; top: 10px;}
#d2 { background: brown; left: 70px; top: 10px;}
#d3 { background: green; left: 10px; top: 70px ;}
#d4 { background: purple; left: 70px; top: 70px ;}
<main>
<div id="d1" class="movable"></div>
<div id="d2" class="movable"></div>
<div id="d3" class="not-movable"></div>
<div id="d4" class="movable"></div>
</main>
ps.: this is not all that much efficient. One can surely iterate on this and create a better version of it!

Related

How to save drag and drop to local storage?

I'm just learning JavaScript. There is a code that can move the block. But I can't save it in localStorage. Help!
let drag = document.querySelector('.note');
drag.onmousedown = function(e) {
let coord = getCoord(drag);
let shiftX = e.pageX - coord.left;
let shiftY = e.pageY - coord.top;
drag.style.position = 'absolute';
document.body.appendChild(drag);
moveNote(e);
drag.style.zIndex = 1000;
function moveNote(e) {
drag.style.left = e.pageX - shiftX + 'px';
drag.style.top = e.pageY - shiftY + 'px';
}
document.onmousemove = function(e) {
moveNote(e);
};
drag.onmouseup = function() {
document.onmousemove = null;
drag.onmouseup = null;
};
}
function getCoord(elem) {
let main = elem.getBoundingClientRect();
return {
top: main.top,
left: main.left
};
}
.note {
width: 50px;
height: 50px;
background: red;
}
<div class="note">
</div>
This seems like very simple process.
On mouse up, get the left and top properties and save their values to localStorage:
document.addEventListener("mouseup", () => {
localStorage.setItem("top", drag.style.top);
localStorage.setItem("left", drag.style.top);
});
let coordinates = localStorage["top"] + "," + localStorage["left"];
if (localStorage["top"] && localStorage["left"]) {
drag.style.top = coordinates.split(",")[0];
drag.style.left = coordinates.split(",")[1];
}
Or the quicker way
document.addEventListener("mouseup", () => {
localStorage.setItem("coordinates", drag.style.top + "," + drag.style.left);
});
let coordinates = localStorage["coordinates"];
if (localStorage["coordinates"]) {
drag.style.top = coordinates.split(",")[0];
drag.style.left = coordinates.split(",")[1];
}
You can save the location using localStorge.setItem("key", "value") and load the item using localStorge.getItem("key"). So we save the JSON representation of our location object. We do that when mouse is up. But also not forgetting to load the value first on page load.
Note: This won't work in this sandbox environment.
let drag = document.querySelector('.note');
drag.onmousedown = function(e) {
let coord = getCoord(drag);
let shiftX = e.pageX - coord.left;
let shiftY = e.pageY - coord.top;
drag.style.position = 'absolute';
document.body.appendChild(drag);
moveNote(e);
drag.style.zIndex = 1000;
function moveNote(e) {
drag.style.left = e.pageX - shiftX + 'px';
drag.style.top = e.pageY - shiftY + 'px';
var position = {
x: drag.style.left,
y: drag.style.top
}
localStorage.setItem("last-position", JSON.stringify(position))
}
document.onmousemove = function(e) {
moveNote(e);
};
drag.onmouseup = function() {
document.onmousemove = null;
drag.onmouseup = null;
};
}
function getCoord(elem) {
let main = elem.getBoundingClientRect();
return {
top: main.top,
left: main.left
};
}
window.onload = function() {
var str_position = localStorage.getItem("last-position") || "{x:0, y:0}"
var position = JSON.parse(str_position);
drag.style.left = position.x;
drag.style.top = position.y;
drag.style.position = 'absolute';
document.body.appendChild(drag);
drag.style.display = 'block'
}
.note {
width: 50px;
height: 50px;
background: red;
display: none;
}
<div class="note">
</div>

Move element with mouse starts to snap up and down

I am trying to make a drag on y axis functionality using mousedown, mousemove events. The formula is as follows:
var position = e.clientY - getOrigin(myDiv).top;
myDiv.style.transform = 'translate3d(0px, ' + position + 'px, 0px)';
function getOrigin(elm) {
...
return {
left: box.left + (win.pageXOffset || docElem.scrollLeft) - clientLeft,
top: box.top + (win.pageYOffset || docElem.scrollTop) - clientTop
};
}
When I drag the element, it snaps up and down really fast. Why is that happening, and how can I fix it?
JSFiddle
var myDiv = document.getElementById('myDiv');
myDiv.addEventListener('mousedown', handleMouseDown);
window.addEventListener('mouseup', handleMouseUp);
function handleMouseDown(e) {
window.addEventListener('mousemove', handleMouseMove);
}
function handleMouseUp(e) {
window.removeEventListener('mousemove', handleMouseMove);
}
function handleMouseMove(e) {
e.preventDefault();
var position = e.clientY - getOrigin(myDiv).top;
myDiv.style.transform = 'translate3d(0px, ' + position + 'px, 0px)';
}
function getOrigin(elm) {
var box = (elm.getBoundingClientRect) ? elm.getBoundingClientRect() : {
top: 0,
left: 0
},
doc = elm && elm.ownerDocument,
body = doc.body,
win = doc.defaultView || doc.parentWindow || window,
docElem = doc.documentElement || body.parentNode,
clientTop = docElem.clientTop || body.clientTop || 0, // border on html or body or both
clientLeft = docElem.clientLeft || body.clientLeft || 0;
return {
left: box.left + (win.pageXOffset || docElem.scrollLeft) - clientLeft,
top: box.top + (win.pageYOffset || docElem.scrollTop) - clientTop
};
}
body {
margin-top: 50px;
padding-top: 50px;
}
#myDiv {
background-color: orange;
width: 200px;
height: 100px;
}
<div id="myDiv"></div>
Because I couldn't exactly figure out what was causing the problem in OPs question (for an unknown reason box.top was returning 2 different values alternately on each pixel movement in OPs script), I've written a different script that works perfectly fine:
//document.addEventListener('DOMContentLoaded', function(){
var myDiv = document.getElementById('myDiv');
myDiv.addEventListener('mousedown', handleMouseDown);
window.addEventListener('mouseup', handleMouseUp);
function handleMouseDown(e) {
window.addEventListener('mousemove', mouseMove.start(this, 'body',e));
}
function handleMouseUp(e) {
window.removeEventListener('mousemove', mouseMove.stop('body'));
}
var mouseMove = function(){
return {
move: function(elm, posY){
elm.style.top = posY + "px";
},
start: function(elm, container, e){
e = e || window.event;
var posY = e.clientY,
elmTop = elm.style.top,
elmHeight = parseInt(elm.style.height),
conHeight = parseInt(document.getElementById(container).style.height);
elmTop = elmTop.replace('px','');
var diffY = posY - elmTop;
document.onmousemove = function(e){
e = e || window.event;
var posY = e.clientY,
Y = posY - diffY;
if (Y < 0) Y = 0;
if (Y + elmHeight > conHeight) Y = conHeight - elmHeight;
mouseMove.move(elm,Y);
}
},
stop : function(container){
var a = document.createElement('script');
document.onmousemove = function(){}
},
}
}();
//});
#body {
margin-top: 50px;
position: absolute;
}
#myDiv {
position: absolute;
background-color: orange;
width: 200px;
height: 100px;
}
<div id="body">
<div id="myDiv"></div>
</div>

How do I convert global coordinates to local?

I have created some simple drag and drop. (There's some junk in the code that seems like it ought to be useful.)
Html:
<div id="container">
<div id="target"></div>
</div>
Javascript:
var el = document.getElementById('target');
var mover = false,
x, y, posx, posy, first = true;
el.onmousedown = function () {
mover = true;
};
document.onmouseup = function () {
mover = false;
first = false;
};
document.onmousemove = function (e) {
if (mover) {
e = e || window.event;
var target = e.srcElement || e.target;
var rect = target.getBoundingClientRect();
if (first) {
first = false;
x = (getMouseCoordinates(e).x - rect.left);
y = (getMouseCoordinates(e).y - rect.top);
}
posx = getMouseCoordinates(e).x;// - x;
posy = getMouseCoordinates(e).y;// - y;
el.style.left = posx + 'px';
el.style.top = posy + 'px';
}
};
function getMouseCoordinates(e) {
e = e || window.event;
var pageX = e.pageX;
var pageY = e.pageY;
if (pageX === undefined) {
pageX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
pageY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
return {
x: pageX,
y: pageY
}
}
CSS:
#target {
left: 50px;
top: 50px;
width: 150px;
height: 100px;
background-color: #ffc;
position: absolute;
}
#container {
left: 30px;
top:30px;
width: 300px;
height: 300px;
background-color: red;
position: absolute;
}
http://jsfiddle.net/YGzm6/
The problem is that the draggable is inside a container that is positioned. So when I position the draggable with the mouse's global co-ordinates, the draggable is actually being positioned relative to its non global container.
So how do I translate the co-ordinates such that the draggable doesn't jump about? Surely I need to know the offset of the parent container?
var x = 0, y = 0;
var element = document.getElementById('container');
do {
x += element.offsetLeft;
y += element.offsetTop;
}
while (element = element.offsetParent);

Simple drag and drop code

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);
}
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.
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>
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
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

drag and drop div element not work well

I've created a simple code to drag and drop div element but not work well.
When you drag the div element quickly to any direction top, left, right, down, the mouse cursor will leave the div element, Although I'm still press on the button .
HTML
<div id="box"></div>
CSS
div#box {
background-color:yellowgreen;
width:150px; height:100px;
border:1px solid #ffff66;
position:relative;
}
JavaScript
var elem = document.getElementById('box');
var PositionX = 0;
var PositionY = 0;
var MouseX = 0
var MouseY = 0;
var mouseDown = false;
elem.onmousedown = function(e) {
PositionX = elem.offsetLeft;
PositionY = elem.offsetTop;
MouseX = e.clientX;
MouseY = e.clientY;
mouseDown = true;
};
elem.onmousemove = function(e) {
if (mouseDown) {
elem.style.left = PositionX + e.clientX - MouseX + "px";
elem.style.top = PositionY + e.clientY - MouseY + "px";
}
};
elem.onmouseover = function(e) {
elem.style.cursor = 'move';
};
elem.onmouseup = function(e) {
mouseDown = false;
};
You can see online
Try changing this one line:
elem.onmousemove = function(e) {
to:
document.onmousemove = function(e) {
try this hope it would have your work done :
var elem= document.getElementbyId('<%=box.ClientID%>');
elem.onmousemove= function(e) {
if (mouseDown) {
elem.style.left = PositionX + e.clientX - MouseX + "px";
elem.style.top = PositionY + e.clientY - MouseY + "px";
}
};

Categories

Resources