I've tried adding the top and left changes, and margin (with different positioning changes too) changes to each element if one approaches another but only the margin change works once. Is there a way to make them work more consistently?
This is where I've used a function to add the event listeners:
var three = document.getElementById('three');
var test = document.getElementById('test');
var two = document.getElementById('two');
var obj = null;
function testmovea(object, event) {
obj = document.getElementById(object.id);
document.addEventListener("mousemove", move);
}
function move(event) {
obj.innerText = event.clientX + ' ' + event.clientY;
obj.style.position = 'absolute';
obj.style.left = event.clientX + "px";
obj.style.top = event.clientY + "px";
three.addEventListener('mouseover', borders);
three.addEventListener('mouseleave', bordersOff);
two.addEventListener('mouseenter', bTwo);
test.addEventListener('mouseenter', bTest);
two.addEventListener('mouseleave', bTwoReset);
test.addEventListener('mouseleave', bTestReset);
document.addEventListener("mouseup", stopMove);
}
function bTwo() {
two.style.margin = "10%";
}
function bTwoReset() {
two.style.margin = "0%";
}
function bTest() {
test.style.margin = "10%";
}
function bTestReset() {
test.style.margin = "0%"
}
This is the mouse stop event I use:
function stopMove() {
document.removeEventListener('mousemove', move);
test.removeEventListener('mouseover', bTest);
two.removeEventListener('mouseover', bTwo);
test.removeEventListener('mouseleave', bTestReset);
two.removeEventListener('mouseleave', bTwoReset);
}
the testmovea function relies on a onmousedown property defined for a DOM element of the page
Update:
I've managed to get it to partially work:
function collide(el1, el2) {
if(twoBoundX >= threeBoundY || threeBoundY >= twoBoundX) {
two.style.margin = + event.clientX + "px";
two.style.margin = + event.clientY + "px";
}
}
where twoBoundX and threeBoundY are getBoundingClientRect() of the respective elements
Here's the full code snippet:
<html>
<head>
<style type="text/css">
#two {
border: 1px solid black;
padding: 1%;
margin: 1%;
margin-top: 10%;
width: 10%;
}
#three {
border: 1px solid black;
padding: 1%;
margin-top: 2%;
}
</style>
</head>
<body>
<div id="two" onmousedown="testmovea(this, event)" onclick="currentTwoPos()">
stop movement
</div>
<div id="three" onmousedown="testmovea(this)">Expand div</div>
<div style="height: 30%" id="two" contenteditable="true">
some contenteditable</div>
</body>
<script>
var three = document.getElementById('three');
var two = document.getElementById('two');
const twoBound = two.getBoundingClientRect();
const threeBound = three.getBoundingClientRect();
var threeBoundX = threeBound.x;
var threeBoundY = threeBound.y;
var twoBoundX = twoBound.x;
var twoBoundY = twoBound.y;
var twoBoundBottom = null;
var twoBoundTop = null;
var obj = null;
function collide(el1, el2) {
if(twoBoundX >= threeBoundY || threeBoundY >= twoBoundX) {
two.style.left = event.clientX + "px";
two.style.top = event.clientY + "px";
three.style.left = event.clientX + "px";
three.style.top = event.clientY + "px";
}
}
function testmovea(object, event) {
obj = document.getElementById(object.id);
document.addEventListener("mousemove", move);
}
function move(event) {
obj.innerText = event.clientX + ' ' + event.clientY;
obj.style.position = 'absolute';
obj.style.left = event.clientX + "px";
obj.style.top = event.clientY + "px";
two.addEventListener('mouseover', collide);
three.addEventListener('mouseover', collide);
document.addEventListener("mouseup", stopMove);
}
function stopMove() {
mousemove = false;
document.removeEventListener('mousemove', move);
three.removeEventListener('mouseover', collide);
two.removeEventListener('mouseover', collide);
}
</script>
</html>
collision detection
You need to define a function that checks whether two of your shapes collide. If you only have rectangles whose vertex are parallel with the OX and OY vertexes, it would look like this:
function areColliding(r1, r2) {
return !(
(r1.x > r2.x + r2.w) ||
(r1.x + r1.w < r2.x) ||
(r1.y > r2.y + r2.h) ||
(r1.y + r1.h < r2.y)
);
}
Of course, if some rotation or even other shapes are involved into your problem, then you need to extend/adjust the collision detector accordingly.
a shared function
You need to create a function that would receive the current status of the elements and the move that happens. It would look like this:
function handleMove(currentPositions, proposedPositions) {
while (!validPositions(proposedPositions)) {
proposedPositions = generateNewPositions(currentPositions, handleCollisions(proposedPositions));
}
refreshUI(proposedPositions);
}
currentPositions is the set of positions your elements currently have
proposedPositions is the set of positions your elements are going to have if there are no collisions
validPositions checks for any pair of shapes that would collide and returns true if none of those pair collide and false if at least one such pair collides
proposedPositions is being refreshed while there are still collisions
generateNewPositions is your handler for collision-based changes
handleCollisions effectuates changes to avoid collision
refreshUI refreshes the UI
event handling
your mouse events should handle change updates by loading all the positions of your elements and calling this shared functionality.
Note: If you have further problems, then you might need to create a reproducible example so we could see your exact structure, styling and code as well.
<html>
<head>
<style type="text/css">
#two {
border: 1px solid black;
width: 10%;
padding: 1%;
margin: 1%;
margin-top: 10%;
}
#three {
border: 1px solid black;
padding: 1%;
margin-top: 2%;
}
</style>
</head>
<body>
<div id="two" onmousedown="testmovea(this, event)" style="position: relative;">
stop movement
</div>
<div id="three" onmousedown="testmovea(this)" style="position: relative;">Expand div</div>
<!--<div style="height: 30%; position: relative;" id="two" contenteditable="true">
some contenteditable</div>-->
</body>
<script>
var three = document.getElementById('three');
var two = document.getElementById('two');
let moveStarted = false;
function collide() {
const first = two.getBoundingClientRect();
const second = three.getBoundingClientRect();
if (!(
(first.x + first.width < second.x) ||
(second.x + second.width < first.x) ||
(first.y + first.height < second.y) ||
(second.y + second.height < first.y)
)) {
two.style.left = event.clientX + "px";
two.style.top = event.clientY + "px";
three.style.left = event.clientX + "px";
three.style.top = event.clientY + "px";
}
}
function testmovea(object, event) {
obj = document.getElementById(object.id);
if (!moveStarted) {
document.addEventListener("mousemove", move);
moveStarted = true;
}
}
function move(event) {
//obj.innerText = event.clientX + ' ' + event.clientY;
obj.style.left = event.clientX + "px";
obj.style.top = event.clientY + "px";
if (!obj.classList.contains("dragged")) obj.classList.add("dragged");
collide(obj);
}
function stopMove() {
mousemove = false;
if (moveStarted) {
document.removeEventListener('mousemove', move);
moveStarted = false;
}
}
document.addEventListener("mouseup", stopMove);
</script>
</html>
Related
The element moves all the directions (top, right, bottom, left) including diagonally. I do not want it to move diagonally.
HTML
<body onload="move('theDiv')">
<div class="container">
<div id="theDiv" class="theDiv"></div>
</div>
</body>
Javascript
var dragValue;
function move(id) {
var element = document.getElementById("theDiv");
element.style.position = "sticky";
element.onmousedown = function() {
dragValue = element;
};
}
document.onmouseup = function(e) {
dragValue = null;
};
document.onmousemove = function(e) {
var x = e.pageX,
y = e.pageY;
dragValue.style.transform = "transltex(" + x + "px)";
dragValue.style.transform = "transltey(" + y + "px)";
};
Use lastPoint.
1. When the value of x in the current position is not equal to the value of x in the old position, that is, moving in the direction of the x-axis.
2. When the value of y of the current position is not equal to the value of y of the old position, it means moving in the direction of the y-axis.
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<style>
.theDiv {
background-color: blue;
height: 100px;
width: 150px;
position: fixed;
top: 0;
left: 0;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body onload="move('theDiv')">
<div id="theDiv" class="theDiv"></div>
<script>
var dragValue;
var lastPoint;
function move(id) {
var element = document.getElementById("theDiv");
element.onmousedown = function(e) {
dragValue = element;
lastPoint = e;
lastPoint.pageX = lastPoint.pageX - ($('#theDiv').width() / 2);
lastPoint.pageY = lastPoint.pageY - ($('#theDiv').height() / 2);
};
}
document.onmouseup = function(e) {
dragValue = null;
lastPoint = null;
};
document.onmousemove = function(e) {
if (dragValue != null) {
var x = e.pageX - ($('#theDiv').width() / 2),
y = e.pageY - ($('#theDiv').height() / 2);
if (lastPoint.pageX == e.pageX)
dragValue.style.top = y + "px";
else
dragValue.style.left = x + "px";
lastPoint = e;
}
};
</script>
</body>
</html>
I had to change your code a bit to be able to run it. You can easily use this code or use it with your own code
Dragging the element seems to make it get stuck randomly before touching the edges of its parent container. Would someone be able to spot why this is going on?
On a side note, I could not get the remembered position transferred to "updateDrag" correctly.
And no, I don't want any jQuery solution, like one created with jQuery UI.
let $container = document.getElementById('container');
let $element = document.getElementById('element');
let mousePosition;
let offset = [0, 0];
let isDown = false;
function initDrag(event) {
isDown = true;
// Element also remembers previous position wrong
// offset = [
// $element.offsetLeft - event.clientX,
// $element.offsetTop - event.clientY
// ];
offset = [
0,
0
];
}
function updateDrag(event) {
event.preventDefault();
if (isDown) {
mousePosition = {
x: event.clientX,
y: event.clientY
};
let diffY = mousePosition.y - offset[1];
let diffX = mousePosition.x - offset[0];
if (diffY >= 0 && diffY + $element.offsetHeight <= $container.offsetHeight) {
$element.style.top = (mousePosition.y + offset[1]) + 'px';
}
if (diffX >= 0 && diffX + $element.offsetWidth <= $container.offsetWidth) {
$element.style.left = (mousePosition.x + offset[0]) + 'px';
}
}
}
function haltDrag() {
isDown = false;
}
$element.addEventListener('mousedown', initDrag, false);
document.addEventListener('mouseup', haltDrag, false);
document.addEventListener('mousemove', updateDrag, false);
#container {
position: relative;
top: 10px;
left: 10px;
height: 300px;
width: 300px;
background-color: green;
}
#element {
position: absolute;
left: 0;
top: 0;
width: 100px;
height: 100px;
background: red;
color: blue;
}
<div id="container">
<div id="element"></div>
</div>
I am trying to create(and position) rectangle divs on a parent div. The created div should be positioned relative. Here is a working jsfiddle example -> Just draw some rectangles by holding mouse button.
var newRect = null;
var offset = $('#page').offset();
function point(x, y) {
this.x = x;
this.y = y;
}
function rect(firstPoint) {
this.firstPoint = firstPoint;
this.div = document.createElement("div");
this.div.style.position = "relative";
this.div.style.border = "solid 1px grey";
this.div.style.top = this.firstPoint.y+"px";
this.div.style.left = this.firstPoint.x+"px";
this.div.style.width = "0px";
this.div.style.height = "0px";
$("#page").append(this.div);
}
$("#page").mousedown(function (e) {
if(e.which == 1) {
var x = e.pageX - offset.left;
var y = e.pageY - offset.top;
newRect = new rect(new point(x, y));
}
});
$("#page").mousemove(function (e) {
if(newRect) {
newRect.div.style.width = Math.abs(newRect.firstPoint.x-(e.pageX - offset.left))+"px";
newRect.div.style.height = Math.abs(newRect.firstPoint.y-(e.pageY - offset.top))+"px";
}
});
$("#page").mouseup(function (e) {
if(e.which == 1 && newRect != null) {
if(Math.abs(newRect.firstPoint.x-(e.pageX - offset.left)) < 10) {
$("#"+newRect.div.id).remove();
newRect = null;
return;
}
$("#"+newRect.div.id).on('mousedown', function (e) {
e.stopImmediatePropagation();
});
newRect = null;
}
});
#page{
width: 210mm;
height: 297mm;
border:solid 2px #6D6D6D;
cursor: crosshair;
background-color: white;
float:left;
background-repeat: no-repeat;
background-size: contain;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="page">
</div>
After drawing the first rectangle, which is positioned correctly, each rectangle is positioned false. I think that there is something wrong with the calculation of the position... maybe someone can give me a hint.
Change
this.div.style.position = "relative";
to
this.div.style.position = "absolute";
Bonus: Here's a version that allows you to draw in any direction: https://jsfiddle.net/g4z7sf5c/5/
I simply added this code to the mousemove function:
if (e.pageX < newRect.firstPoint.x) {
newRect.div.style.left = e.pageX + "px";
}
if (e.pageY < newRect.firstPoint.y) {
newRect.div.style.top = e.pageY + "px";
}
I would like it to create the div where the mouse is. I have the following code:
var mouseisdown = false;
$(document).mousedown(function(event) {
mouseisdown = true;
doSomething();
}).mouseup(function(event) {
mouseisdown = false;
});
function doSomething(e){
var draw = document.createElement("div");
draw.className = "draw";
document.body.appendChild(draw);
draw.style.top = e.clientY + "px";
draw.style.left = e.clientX + "px";
if (mouseisdown)
doSomething();
}
Basically you already had it, but you overcomplicated it:
Remove the mouseisdown variable and the event listeners
Add doSomething as a click event listener
Don't call doSomething recursively
$(document).click(function doSomething(e){
var draw = document.createElement("div");
draw.className = "draw";
document.body.appendChild(draw);
draw.style.top = e.clientY + "px";
draw.style.left = e.clientX + "px";
});
.draw {
position: absolute;
height: 10px;
width: 10px;
margin: -5px;
background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Click somewhere
I am trying to create new circles as my pen hovers on window.
I am having issues where I cannot add circles to the page. It just hovers around. How would i be able to modify my code to add circles as it hovers.
<!doctype html>
<html>
<head>
<title> JavaScript Environment: Project </title>
<meta charset="utf-8">
<style>
html, body {
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
}
#canvas {
background-color: yellow;
width: 100%;
height: 100%;
}
.pen {
position: absolute;
background-color: lightblue;
width: 10px;
height: 10px;
border-radius: 5px;
}
</style>
<script>
window.onload = function() {
function Circle(x, y) {
this.x = x;
this.y = y;
}
var canvas = document.getElementById("canvas");
canvas.onmousedown = function() {
mouseDown();
};
canvas.onmouseup = function() {
mouseUp()
};
canvas.onmousemove = function() {
mouseMove(event)
};
function mouseDown (){
console.log ("mouse down");
}
function mouseUp (){
console.log ("mouse up");
}
function mouseMove(e) {
var canvas = document.getElementById("canvas");
var pen = document.createElement("div");
var x = e.clientX;
var y = e.clientY;
var coor = "Coordinates: (" + x + "," + y + ")";
pen.setAttribute("class", "pen");
pen.style.left = x + "px";
pen.style.top = y + "px";
document.getElementById("canvas").innerHTML = coor;
canvas.appendChild(pen);
addCircles(x, y);
console.log("location # " + x + " : " + y);
}
function addCircles(x, y) {
var canvas = document.getElementById("canvas");
var circle = document.createElement("div");
circle.setAttribute("class", "pen");
circle.style.left = x + "px";
circle.style.top = y + "px";
canvas.appendChild(circle);
}
canvas.addEventListener("mouseMove", mouseMove, false);
};
</script>
</head>
<body>
<div id="canvas"></div>
</body>
</html>
The problem is in the line document.getElementById("canvas").innerHTML = coor;
Try adding a span <span id="canvasText"></span> inside of your canvas div and then changing the above line to document.getElementById("canvasText").innerHTML = coor;.
As it stands, you "reset" the contents of the canvas every time the mouse moves, so the circles are instantly removed from it. Reset only the span inside the canvas to keep the circles around.