How to detect when a mouse moves away from the element - javascript

mousemove is fired when mouse is moving over an element. How can I detect when the mouse is moving outside of an element? In other words, anywhere on the page besides the div in the snippet. Not when the mouse leaves but fires whenever the mouse is moving outside of the element.
const div = document.querySelector('div');
div.addEventListener('mousemove', function() {
document.body.classList.add('mouse-moving');
});
div {
height: 200px;
width: 300px;
background-color: red;
}
.mouse-moving {
background-color: green;
}
<div></div>

You can use onmouseover and onmouseout
const div = document.querySelector('div');
div.onmouseover = ()=> document.body.classList.add('mouse-moving');
div.onmouseout = ()=> document.body.classList.remove('mouse-moving');
div {
height: 200px;
width: 300px;
background-color: red;
}
.mouse-moving {
background-color: green;
}
<div></div>

You can add the mousemove event listener to the document and check whether the event target is your div or not:
const div = document.querySelector('div');
document.addEventListener('mousemove', function(e) {
if(e.target !== div) {
div.textContent = "outside the div (" + e.clientX + ", " + e.clientY + ")";
} else {
div.textContent = "inside the div (" + e.clientX + ", " + e.clientY + ")";
}
});
div {
height: 200px;
width: 300px;
background-color: red;
}
<div></div>
Note: If the div contain other element, the test won't work. You'll have to check if one of the target's ancestors is your div:
document.addEventListener('mousemove', function(e) {
var elm;
for(elm = e.target; elm && elm !== div; elm = elm.parentElement)
;
if(elm === div) {
// inside div
} else {
// outside div
}
});
const div = document.querySelector('div'),
result = document.querySelector("#result");
document.addEventListener('mousemove', function(e) {
var elm;
for(elm = e.target; elm && elm !== div; elm = elm.parentElement)
;
if(elm === div) {
result.textContent = "inside the div (" + e.clientX + ", " + e.clientY + ")";
} else {
result.textContent = "outside the div (" + e.clientX + ", " + e.clientY + ")";
}
});
div {
height: 200px;
width: 300px;
background-color: red;
}
div > div {
height: 50px;
width: 50px;
background-color: blue;
margin: 5px;
}
<span id="result"></span>
<div>
<div></div>
<div></div>
</div>
Also, if the div's children are outside its boundary (due to some absolute positioning or something), the above method won't work, you'll have to check if the mouse coordinates are inside the div's bounding rectangle.

Related

How to use vanilla script to bounce elements from each other

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>

How Can Conflict Between Mouse Events Be Resolved

I have a div called starter-box that when clicked is supposed to dynamically create 2 divs. I added an event listener to the parent of the starter-box div to listen for any event of the 2 dynamically created divs.
The problem is that the click event listener conflicts with the mousedown event listener preventing it (the click event) from executing.
document.addEventListener('DOMContentLoaded', function () {
var container = document.querySelector("#container");
var target;
var elementCreated = false;
var elements = [];
container.addEventListener("click", (ev) => {
target = ev.target;
if (target.className != "box" && target.id != "container") {
if (elementCreated == false) {
createElements(target);
} else if (elementCreated == true) {
removeElementsByClass("box");
elements = [];
}
}
});
container.addEventListener("mousedown", (ev) => {
target = ev.target;
alert("MouseDown on: " + target.id);
});
container.addEventListener("mouseup", (ev) => {
target = ev.target;
alert("MouseUp on: " + target.id);
});
function createElements(target) {
const dimensions = target.getBoundingClientRect();
const element1 = document.createElement('div');
element1.className = "box";
element1.id = "box1";
element1.style.left = (dimensions.left) + "px";
element1.style.top = dimensions.bottom + 25 + "px";
container.appendChild(element1);
const element2 = document.createElement('div');
element2.className = "box";
element2.id = "box2";
element2.style.left = (dimensions.left) + "px";
element2.style.top = dimensions.bottom + 90 + "px";
container.appendChild(element2);
}
});
#container {
width: 600px;
height: 400px;
background-color: blue
}
#starter-box {
position: relative;
top: 50px;
left: 25px;
width: 50px;
height: 50px;
background-color: red
}
.box {
position: absolute;
width: 50px;
height: 50px;
background-color: black
}
<div id="container">
<div id="starter-box">
</div>
</div>
Check the fields of event object (ev argument to the listener callback).
In case of 'click' event: ev.type === 'click'
In case of 'mousedown' event: ev.type === 'mousedown'
You can use it your logic.
Discover the event object, try to console.log(ev), and see. There are more differences.

How to prevent a div from moving up when zooming in

So for starters this question may seem kind of vague, so I am going to do the best I can to describe my situation.
I have created a html page with adjustable sections (top left, top right, and bottom). There are dividers to adjust the X axis (between the top two sections) and to adjust the Y axis (between the top and bottom section). Everything appears to be working fine, however when I zoom in (using command + on the keyboard), it pushes the text within the bottom section above the Y axis adjuster.
I want instead for the text to be contained in the div beneath the Y axis adjuster and never slide on top of it (even when the screen is resized or zoomed in on).
The above image is what it looks like before zooming in. Notice how the text 'Element 3' is below the green line (Y axis adjuster).
The above image is what it looks like after zooming in. Notice how the text 'Element 3' raises above the green line (Y axis adjuster).
The error should be in the HTML code, not the JS, however I included the JS files for good measure.
I looked all over the internet for a solution but came up empty handed. I have tried simply bootstrapping the page by including the appropriate script tags but that did not work, as well as adjusting various css styles.
html file:
<!doctype html>
<head>
<title>resizer</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<script type="text/javascript" src="Common.js"></script>
<script type="text/javascript" src="resizer.js"></script>
<style>
.element{
border: 1px solid #999999;
border-radius: 4px;
margin: 5px;
padding: 5px;
}
</style>
<script type="text/javascript">
function onresize() {
var element1 = document.getElementById("element1");
var element2 = document.getElementById("element2");
var element3 = document.getElementById("element3");
var ResizerY = document.getElementById("resizerY");
ResizerY.style.top = element3.offsetTop - 15 + "px";
var topElements = document.getElementById("topElements");
topElements.style.height = ResizerY.offsetTop - 20 + "px";
var height = topElements.clientHeight - 32;
if (height < 0)
height = 0;
height += 'px';
element1.style.height = height;
element2.style.height = height;
}
function resizeX(x) {
var element2 = document.getElementById("element2");
element2.style.width =
element2.parentElement.clientWidth
+ document.getElementById('rezizeArea').offsetLeft
- x
+ 'px';
}
function resizeY(y) {
var element3 = document.getElementById("element3");
var height =
element3.parentElement.clientHeight
+ document.getElementById('rezizeArea').offsetTop
- y
;
if ((height + 100) > element3.parentElement.clientHeight)
return;//Limit of the height of the elemtnt 3
element3.style.height = height + 'px';
onresize();
}
var emailSubject = "Resizer example error";
</script>
</head>
<body>
<div id="rezizeArea" style="top=0; bottom=0; right=0; left=0; width:100%; height:100%; overflow:hidden; position: absolute;" class="element">
<div id="topElements" class="element" style="overflow:auto; position:absolute; left: 0; top: 0; right:0;">
<div id="element2" class="element" style="width: 30%; height:10px; float: right; position: relative;">
Element 2
</div>
<div id="resizerX" style="width: 10px; height:100%; background: red; float: right;"></div>
<script type="text/javascript">
resizerX("resizerX", function (e) {
resizeX(e.pageX + 25);
});
</script>
<div id="element1" class="element" style="height:10px; overflow:auto;">
Element 1
</div>
</div>
<div id="resizerY" style="height:10px; position:absolute; left: 0; right:0; background: green;"></div>
<script type="text/javascript">
resizerY("resizerY", function (e) {
resizeY(e.pageY + 25);
});
</script>
<div id="element3" class="element" style="display: inline-block; height:100px; position:absolute; left: 0; bottom: 0; right:0;">
Element 3
</div>
</div>
<script type="text/javascript">
onresize();
</script>
<hr>
</body>
</html>
resizer.js:
function resizer(resizerID, mousemove, cursor) {
consoleLog("resizer(" + resizerID + ")");
var resizer = document.getElementById(resizerID);
resizer.style.cursor = cursor;
resizer.mousemove = mousemove;
resizer.onmousedown = function (e) {
try{
consoleLog("resizer.onmousedown(e)");
document.documentElement.addEventListener('mousemove', resizer.doDrag, false);
document.documentElement.addEventListener('mouseup', resizer.stopDrag, false);
} catch (e) {
ErrorMessage("resizer.onmousedown(...) failed! Your browser does not support this feature. " + e.message);
}
}
resizer.doDrag = function (e) {
if (e.which != 1){
consoleLog("mouseup");
resizer.stopDrag(e);
return;
}
//consoleLog("doDrag(e)");
resizer.mousemove(e);
}
resizer.stopDrag = function (e) {
consoleLog("stopDrag(e)");
document.documentElement.removeEventListener('mousemove', resizer.doDrag, false);
document.documentElement.removeEventListener('mouseup', resizer.stopDrag, false);
}
}
function resizerX(resizerID, mousemove) {
resizer(resizerID, mousemove, "e-resize");
}
function resizerY(resizerID, mousemove) {
resizer(resizerID, mousemove, "n-resize");
}
Common.js:
function MessageElement(Message) {
var element = document.getElementById('Message');
if (element == null) {
alert('ERROR: element Message == null. ' + Message);
return;
}
if (element.innerHTML != Message)
{
//consoleLog('Message: ' + Message);
element.innerHTML = Message + '<hr><div align="center"><input type="button" onclick="javascript: return MessageElement(\'\')" value="Close" style="margin-top:5px;" /></div>';
if (Message == "")
element.style.display = "none";
else element.style.display = "block";
}
}
function ErrorMessage(message, emailMe) {
consoleError(message);
//http://www.htmlhelp.com/reference/html40/entities/special.html
var body;
if (emailMe != false) {
body = message;
body = body.replace(/\n/g, "%0D%0A");
body = body.replace(/ /g, "%20");
body = body.replace(/"/g, "%22");
body = body.replace(/&/g, "%26");
}
message = message.replace(/</g, '<');
message = message.replace(/>/g, '>');
message = message.replace(/\n/g, '<BR>');
if (emailMe != false) {
//http://www.rapidtables.com/web/html/mailto.htm
if (typeof myEmail == 'undefined')
myEmail = "XXX";
message += "<BR><BR><a href=\"mailto:" + myEmail + "?subject=" + emailSubject + "&body=" + body + "\" >Email me about problem</a>"
}
MessageElement('<FONT style="color: red; background-color: white">ERROR: ' + message + '</FONT>');
}
function consoleLog(message) {
try{
console.log(message);//Do not works in WP
} catch(e) {
}
}
function consoleError(msg)
{
try
{
console.error(msg);
} catch(e) {
// alert(msg);
}
}

Div placement in javascript

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

When I want to make a draggable div I couldn't avoid selecting neighboured text

I am a JS beginner.
I have made a draggable <div>. When I drag it, it will select one or two words near the <div> or glint with the blue select box (see the code-snippet below).
It bothers the experience feeling a little. What I want most is to keep other normal words being selected when dragging dhe <div>.
Please help me optimize the code or tell me if there is any syntax mistake.
window.onload = function() {
var parent = document.getElementById('parent');
eventFunc.addEventListener(parent, 'mousedown', dragItem);
}
function dragItem(ev) {
ev = ev || window.event;
var element = eventFunc.target(ev);
var spaceX = ev.clientX - element.offsetLeft,
spaceY = ev.clientY - element.offsetTop,
maxX = (document.documentElement.clientWidth || document.body.clientWidth) - element.offsetWidth,
maxY = (document.documentElement.clientHeight || document.body.clientHeight) - element.offsetHeight;
document.onmousemove = function(ev) {
ev = ev || window.event;
eventFunc.stopPropagation(ev);
element.style.left = (ev.clientX - spaceX) + 'px';
element.style.top = (ev.clientY - spaceY) + 'px';
if (element.offsetLeft < 0) {
element.style.left = 0;
} else if (element.offsetLeft > maxX) {
element.style.left = maxX + 'px'
}
if (element.offsetTop < 0) {
element.style.top = 0;
} else if (element.offsetTop > maxY) {
element.style.top = maxY + 'px'
}
}
document.onmouseup = function() {
document.onmousemove = null;
document.onmouseup = null;
}
}
var eventFunc = {
addEventListener: function(element, type, func) {
if (addEventListener) {
element.addEventListener(type, func, false);
} else if (attachEvent) {
element.attachEvent('on' + type, func);
} else {
element['on' + type] = func;
}
},
target: function(ev) {
return ev.target || ev.srcElement;
},
stopPropagation: function(ev) {
if (ev.stopPropagation) {
ev.stopPropagation();
} else {
ev.cancelBubble = true;
}
},
preventDefault: function(ev) {
if (ev.preventDefault) {
ev.preventDefault();
} else {
ev.returnValue = false;
}
}
}
#parent {
width: 400px;
height: 300px;
position: absolute;
border: 3px solid #ccc;
top: 0;
left: 0;
cursor: all-scroll;
text-align: center;
font: bold 35px/35px'Segoe UI', arial, helvetica, sans-serif;
color: #ccc;
}
#children {
text-align: left;
color: initial;
font: initial;
cursor: auto;
margin: 1em;
border: 1px solid red;
}
Outside Words
<div id="parent">
<div id="children">I'm The Children Element. I'm The Children Element. I'm The Children Element. I'm The Children Element. I'm The Children Element. I'm The Children Element. I'm The Children Element. I'm The Children Element. I'm The Children Element.</div>
DRAG ME
</div>
</body>
inside your dragItem function.... add the following line...
function dragItem(ev) {
ev.preventDefault()
}
[edit (fulfilling the rest of the question)]
To select contents of any piece of content... you can use this function...
function selectNode(el) {
var range = document.createRange()
range.selectNodeContents(el)
var sel = window.getSelection()
sel.removeAllRanges()
sel.addRange(range)
}
// use it by passing in a node
selectNode(document.getElementById('parent'))

Categories