I can make a box that follows the mouse with the following.
document.addEventListener('mousemove', (e) => {
document.documentElement.style.setProperty('--mouse-x', e.clientX + 'px');
document.documentElement.style.setProperty('--mouse-y', e.clientY + 'px');
});
.box {
width: 50px;
height: 50px;
background-color: blue;
transform: translate(calc(var(--mouse-x) - 50%), calc(var(--mouse-y) - 50%));
}
<div class="box"></div>
but as soon as the element is not positioned at the top left, it breaks.
document.addEventListener('mousemove', (e) => {
document.documentElement.style.setProperty('--mouse-x', e.clientX + 'px');
document.documentElement.style.setProperty('--mouse-y', e.clientY + 'px');
});
.box {
width: 50px;
height: 50px;
margin-left: 150px;
background-color: blue;
transform: translate(calc(var(--mouse-x) - 50%), calc(var(--mouse-y) - 50%));
}
<div class="box"></div>
How can I use absolute coordinates with a transform? I don't want to use left/top/position: fixed/absolute because I need to preserve the position of the element in the flow.
I could use JavaScript to grab the central position and then use that infomation to get the correct center.
document.addEventListener('mousemove', (e) => {
document.documentElement.style.setProperty('--mouse-x', e.clientX + 'px');
document.documentElement.style.setProperty('--mouse-y', e.clientY + 'px');
});
window.addEventListener('load', (e) => {
const box = document.querySelector('.box');
const rect = box.getBoundingClientRect();
box.setAttribute('style', `
--center-x: ${rect.left + (rect.width / 2)}px;
--center-y: ${rect.top + (rect.height / 2)}px;
`);
});
.box {
width: 50px;
height: 50px;
margin-left: 150px;
background-color: blue;
transform: translate(calc(var(--mouse-x) - var(--center-x)), calc(var(--mouse-y) - var(--center-y)));
}
<div class="box"></div>
This works, but it's not ideal and easily broken if anything else in the page changes. It also slows down with more elements, and I'd like it to be as fast as possible. It there a better way to do this? I'm fine using CSS/Vanilla JS.
You don't have to use translate() please I would recommend you to use the left and top properties in CSS. They can help you position an element based on coordinates.
window.addEventListener('load', (e) => {
const box = document.querySelector('.box');
const rect = box.getBoundingClientRect();
document.addEventListener('mousemove', (e) => {
box.style.left = e.pageX + 'px';
box.style.top = e.pageY + 'px';
});
});
.box {
width: 50px;
height: 50px;
position:absolute;
transform:translate(-50%,-50%);
background-color: blue;
}
<div class="box"></div>
The transform: translate() property works relative to the size of the box but the left and top properties don't. It can also be much faster in some cases because in your code there was a lot of calculation going on. Whereas, this is straightforward.
Here is a solution using the requestAnimationFrame() function. The requestAnimationFrame() method tells the browser that you wish to perform an animation and requests that the browser calls a specified function to update an animation before the next repaint. For higher refresh rate monitors, this function will be run more the number of times to match the refresh rate.
More information here
Here is the working solution:
const box = document.querySelector('.box');
const rect = box.getBoundingClientRect();
let mouseX = 0;
let mouseY = 0
document.addEventListener('mousemove', (e) => {
mouseX = e.pageX + 'px';
mouseY = e.pageY + 'px';
})
function mouseMove() {
box.style.left = mouseX;
box.style.top = mouseY;
requestAnimationFrame(mouseMove)
};
mouseMove()
.box {
width: 50px;
height: 50px;
position: absolute;
transform: translate(-50%, -50%);
background-color: blue;
}
<div class="box"></div>
Related
The following code always shows the coordinates of the cursor below the cursor:
function showCoords(e) {
var x = event.clientX;
var y = event.clientY;
var coor = "(" + x + ", " + y + ")";
document.getElementById("box").innerHTML = coor;
var bx = document.getElementById("box");
bx.style.left = e.pageX - 50;
bx.style.top = e.pageY + 20;
}
function clearCoords() {
document.getElementById("box").innerHTML = "";
}
div.relative {
position: relative;
width: 400px;
height: 300px;
border: 1px solid black;
}
div.abs {
position: absolute;
top: 100px;
right: 50px;
width: 200px;
height: 100px;
background-color: yellow;
}
<body onmousemove="showCoords(event)">
<div class="relative">
<div class="abs" onmousemove="showCoords(event)" onmouseout="clearCoords()"></div>
</div>
<div id="box" style="width:100px; height:30px; position:absolute"></div>
</body>
I only want the coordinates to be visible when the mouse pointer is hovering over the yellow rectangle.
If I change <body onmousemove="showCoords(event)"> to <body>, the coordinates are never visible.
How do I get the coordinates be visible only when hovering over the yellow rectangle?
Move the onmousemove listener from the body to the element you want to listen on - div.abs in this case.
I'd recommend not using the onmousemove attribute, in favour of using an entirely javascript solution - just to keep javascript-y things together. Something like (untested)
var listenOn = document.querySelector(".abs");
listenOn.addEventListener("mousemove", ShowCoords);
I'm trying to get my png to move to the mouse click position when the user clicks within the container but I cant get the png to respond. I'm following this tutorial (https://www.youtube.com/watch?v=b4GwvdhrEQg), and stuck on the first test. my target doesnt respond to clicks at all.
Please help
var theGirl = document.querySelector("#girl");
var container = document.querySelector("#floor");
container.addEventListener("click", getClickPosition, false);
function getClickPosition(e) {
var xPosition = e.clientX - (theGirl.offsetWidth / 2);
var yPosition = e.clientY; - (theGirl.offsetHeight / 2)
var translate3dValue = "translate3d(" + xPosition + "px" + yPosition + "px, 0)";
theGirl.style.transform = translate3dValue;
}
#floor {
width: 700px;
height: 600px;
cursor: pointer;
overflow: visible;
border: 10px #EDEDED solid;
}
#girl {
height: 450px;
width: 200px;
border: 15px red solid;
transform: translate3d(50px, 50px, 0);
}
<body>
<div id="floor">
<div>
<img src="girl.png" id="girl"> </div>
</div>
Change #girl's position to absolute. I think it works after that.
I am working on a small task, where I have to drag (translate) an element anywhere in the document freely. I have done the basic work but confused about the current position of the mouse. Because when I start dragging the element, the mouse position is not on the spot where the mousedown occurs.
Simply, I want the position of the mouse to stay on where I clicked on the box.
Here's the JSFiddle link.
Well you need to calculate the width and height of the element in order to keep the cursor in its center, note that now it's working with any width and height values, here i have added an animation just resizing width and height to see that always we get the center of the element
let target = document.querySelector(".drag");
function onDrag(e) {
// we could make them global variables instead
const {width, height} = window.getComputedStyle(target);
target.style.transform = `translate(${e.clientX - +width.replace("px", "") / 2}px, ${e.clientY - +height.replace("px", "") / 2}px)`;
}
function onLetGo() {
document.removeEventListener('mousemove', onDrag);
document.removeEventListener('mouseup', onLetGo);
}
function onGrab() {
document.addEventListener('mousemove', onDrag);
document.addEventListener('mouseup', onLetGo);
}
target.addEventListener('mousedown', onGrab);
* {
box-sizing: border-box;
}
.drag{
width: 100px;
height: 100px;
border: 1px solid black;
background: blue;
cursor: pointer;
position: fixed;
animation-name: resize;
animation-duration: 4s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
#keyframes resize {
0% {width: 100px}
25% {height: 150px}
50% {width: 150px}
100% {height: 100px}
}
<div class="drag"></div>
Edited to answer the comment of the OP
Thanks for the answer, it works fine. Is there any way, we can drag the div from any place where we click the div?
So now you want to drag the element from the clicked point and not from its center you can subtract event.offsetX from event.clientX to get the correct cursor position and the same for the y axis, and make sure there is no margin or padding for the containers, in this example I have removed the margin and padding from the HTML and BODY elements
let target = document.querySelector(".drag"), x = 0, y = 0;
function onDrag(e) {
// we use the coords of the mousedown event
target.style.transform = `translate(${e.clientX - x}px, ${e.clientY - y}px)`;
}
function onLetGo() {
document.removeEventListener('mousemove', onDrag);
document.removeEventListener('mouseup', onLetGo);
}
function onGrab(e) {
// we store the point of click(coords)
x = e.offsetX, y = e.offsetY;
document.addEventListener('mousemove', onDrag);
document.addEventListener('mouseup', onLetGo);
}
target.addEventListener('mousedown', onGrab);
* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
}
.drag{
width: 100px;
height: 100px;
border: 1px solid black;
background: blue;
cursor: pointer;
position: fixed;
}
<div class="drag"></div>
Well because transition is affected by margin, padding and other flow control rules you can avoid using it and just use the left and top rules to properly position your element like this
let target = document.querySelector(".drag"), x = 0, y = 0;
function onDrag(e) {
// use the `left` and `top` rules to properly position your element, so
// you no more care about other flow affecting rules
target.style.left = `${e.clientX - x}px`;
target.style.top = `${e.clientY - y}px`;
}
function onLetGo() {
document.removeEventListener('mousemove', onDrag);
document.removeEventListener('mouseup', onLetGo);
}
function onGrab(e) {
x = e.offsetX, y = e.offsetY;
document.addEventListener('mousemove', onDrag);
document.addEventListener('mouseup', onLetGo);
}
target.addEventListener('mousedown', onGrab);
.drag {
width: 100px;
height: 100px;
border: 1px solid black;
background: blue;
cursor: pointer;
position: fixed;
}
<div class="drag"></div>
I have a table full of data that tends to be larger than the screen.
I put the table in a DIV and set the "overflow" to "auto" in CSS
div.scrolling-comps {
width : 970px;
height : 800px;
overflow : auto;
}
So the DIV can be scrolled up/down, left right using the browser's built-in scroll bars.
Problem is, the table can be WAAY bigger than the screen. And while the mousewheel will scroll it up/down, scrolling left/right is a pain in the hooch.
So, looking for a javascript/jquery or CSS way to scroll the div NATURALLY.
In other words, when someone viewing the huuuge table moves their mouse to the right, the DIV goes to the left (thus scrolling without using the scroll bars).
Something similar to this, but instead of following the mouse, the div would move opposite the mouse...
window.onload = function() {
var bsDiv = document.getElementById("box-shadow-div");
var x, y;
// On mousemove use event.clientX and event.clientY to set the location of the div to the location of the cursor:
window.addEventListener('mousemove', function(event) {
x = event.clientX;
y = event.clientY;
if (typeof x !== 'undefined') {
bsDiv.style.left = x + "px";
bsDiv.style.top = y + "px";
}
}, false);
}
#box-shadow-div {
position: fixed;
width: 1000px;
height: 800px;
border-radius: 0%;
background-color: black;
box-shadow: 0 0 10px 10px black;
top: 49%;
left: 48.85%;
}
<div id="box-shadow-div"></div>
The example you have about using the mouse position is interesting... But it is not what you need to achieve what you described.
In fact... What you need to know is the "ratio" between the div wrapping the table and its scrollWidth
Then, using the X position of the mouse, you can apply a scroll to the div in order to make it "move".
I used jQuery to do it using very few lines.
// Just to fake a big table
var fakeCell = $("<td>Some data</td>");
for(i=0;i<100;i++){
var fakeRow = $("<tr>");
for(k=0;k<50;k++){
fakeRow.append(fakeCell.clone().append(" "+k));
}
$("#test").append(fakeRow.clone());
}
// ---------------------------------------------------
// Calculate the "ratio" of the box-div width versus its scrollable width
var ratio = $("#box-div")[0].scrollWidth / $("#box-div").width();
console.log("Ratio: "+ratio);
// Scroll left/rigth based on mouse movement
$(window).on("mousemove", function(e){
var X = ratio * e.pageX;
// Scroll the div using the mouse position multiplyed by the ratio
$("#box-div").scrollLeft(X);
});
td{
white-space: nowrap;
border: 1px solid black;
}
#box-div{
overflow:auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div id="box-div">
<table id="test">
</table>
</div>
</body>
So while the user moves the mouse over the div's width, you apply a scroll multiplied by the ratio... The effect is the user can scroll it all from the most left to most right ends easilly.
How about this?
wrap a table in div (i.e. parent-div) which is relatively positioned
Give position absolute to the target div.
And change left & top position of target div on mousemove event.
window.onload = function() {
var bsDiv = document.getElementById("box-shadow-div");
var x, y;
// On mousemove use event.clientX and event.clientY to set the location of the div to the location of the cursor:
window.addEventListener('mousemove', function(event) {
x = event.clientX;
y = event.clientY;
if (typeof x !== 'undefined') {
bsDiv.style.left = -x + "px";
bsDiv.style.top = -y + "px";
}
}, false);
}
.parent-div {
position: relative;
}
#box-shadow-div {
position: absolute;
width: 1000px;
height: 800px;
border-radius: 0%;
background-color: black;
box-shadow: 0 0 10px 10px black;
top: 0;
left: 0;
}
<div class="parent-div">
<div id="box-shadow-div"></div>
</div>
Have you tried changing x to -x? this will technically "invert" the effect.
window.onload = function() {
var bsDiv = document.getElementById("box-shadow-div");
var x, y;
// On mousemove use event.clientX and event.clientY to set the location of the div to the location of the cursor:
window.addEventListener('mousemove', function(event) {
x = event.clientX;
y = event.clientY;
if (typeof x !== 'undefined') {
bsDiv.style.left = -x + "px";
bsDiv.style.top = -y + "px";
}
}, false);
}
#box-shadow-div {
position: fixed;
width: 1000px;
height: 800px;
border-radius: 0%;
background-color: black;
box-shadow: 0 0 10px 10px black;
top: 49%;
left: 48.85%;
}
<div id="box-shadow-div"></div>
I create table, handlers hooked up mousemove.
But in top left point I get .offsetX.offsetY equals -5-5.
Why? I need 0\0.
<table cellpadding="0"
id="target"
cellspacing="0"
width="602"
height="500"
style="float:left;
position:relative;
background: url(/content/games/kamikaze2/back.jpg) no-repeat 0 0;">
<tbody>...
</tbody>
</table>
<script type="text/javascript">
$("#target").mousemove(function (event) {
var msg = "Handler for .mousemove() called at ";
msg += event.offsetX + ", " + event.offsetY;
$("#log").append("<div>" + msg + "</div>");
});
</script>
More examples here: show-popup-on-mouse-location
Mouse coordinates within element
The most accurate way to get the mouse coordinates within an element (without scrollbars) relative to viewport is by calculating the difference between
MouseEvent.clientX, MouseEvent.clientY and
Element.getBoundingClientRect()
const getMousePos = (evt) => {
const pos = evt.currentTarget.getBoundingClientRect();
return {
x: evt.clientX - pos.left,
y: evt.clientY - pos.top
};
};
document.querySelector("#target").addEventListener('mousemove', (evt) => {
const mPos = getMousePos(evt);
evt.currentTarget.textContent = `Mouse position x:${mPos.x} y:${mPos.y}`;
});
#target {
position: absolute;
left: 50px;
top: 20px;
width: 200px;
height: 100px;
background: #0bf;
transform: translate(20px, 30px); /* works even with translate */
}
<div id="target"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Subtracting those values you can retrieve the exact mouse position, x:0, y:0 being the upper left corner of your element. Even if an element is CSS-transformed, i.e: transform: translate(-50%, -50%), the returned values are still correct.