Javascript - Track mouse position
The mouse's position is reported on the event
object received by a handler for the mousemove
event, which you can attach to the window (the event bubbles):
(function() {
document.onmousemove = handleMouseMove;
function handleMouseMove(event) {
var eventDoc, doc, body;
event = event || window.event; // IE-ism
// If pageX/Y aren't available and clientX/Y are,
// calculate pageX/Y - logic taken from jQuery.
// (This is to support old IE)
if (event.pageX == null && event.clientX != null) {
eventDoc = (event.target && event.target.ownerDocument) || document;
doc = eventDoc.documentElement;
body = eventDoc.body;
event.pageX = event.clientX +
(doc && doc.scrollLeft || body && body.scrollLeft || 0) -
(doc && doc.clientLeft || body && body.clientLeft || 0);
event.pageY = event.clientY +
(doc && doc.scrollTop || body && body.scrollTop || 0) -
(doc && doc.clientTop || body && body.clientTop || 0 );
}
// Use event.pageX / event.pageY here
}
})();
(Note that the body of that if
will only run on old IE.)
Example of the above in action - it draws dots as you drag your mouse over the page. (Tested on IE8, IE11, Firefox 30, Chrome 38.)
If you really need a timer-based solution, you combine this with some state variables:
(function() {
var mousePos;
document.onmousemove = handleMouseMove;
setInterval(getMousePosition, 100); // setInterval repeats every X ms
function handleMouseMove(event) {
var dot, eventDoc, doc, body, pageX, pageY;
event = event || window.event; // IE-ism
// If pageX/Y aren't available and clientX/Y are,
// calculate pageX/Y - logic taken from jQuery.
// (This is to support old IE)
if (event.pageX == null && event.clientX != null) {
eventDoc = (event.target && event.target.ownerDocument) || document;
doc = eventDoc.documentElement;
body = eventDoc.body;
event.pageX = event.clientX +
(doc && doc.scrollLeft || body && body.scrollLeft || 0) -
(doc && doc.clientLeft || body && body.clientLeft || 0);
event.pageY = event.clientY +
(doc && doc.scrollTop || body && body.scrollTop || 0) -
(doc && doc.clientTop || body && body.clientTop || 0 );
}
mousePos = {
x: event.pageX,
y: event.pageY
};
}
function getMousePosition() {
var pos = mousePos;
if (!pos) {
// We haven't seen any movement yet
}
else {
// Use pos.x and pos.y
}
}
})();
As far as I'm aware, you can't get the mouse position without having seen an event, something which this answer to another Stack Overflow question seems to confirm.
Side note: If you're going to do something every 100ms (10 times/second), try to keep the actual processing you do in that function very, very limited. That's a lot of work for the browser, particularly older Microsoft ones. Yes, on modern computers it doesn't seem like much, but there is a lot going on in browsers... So for example, you might keep track of the last position you processed and bail from the handler immediately if the position hasn't changed.
JavsScript — Track mouse position relative to the middle of the screen
Here is one way to do it. Divide mouse x-position with windowWidth so the position is between 0-1. Start from -1 and add xValue/windowWidth*2 so it is mapped between -1 and 1. With y position start from 1 so top 1 and bottom is -1.
onmousemove = function (e) {
let windowHeight = window.innerHeight
let windowWidth = window.innerWidth
let xValue = e.x
let yValue = e.y
let mousePosX = -1+(xValue/windowWidth)*2
let mousePosY = 1-(yValue/windowHeight)*2
};
How do I properly get the mouse position in Javascript?
If you want to get the mouse position relative to the canvas top-left point, you should use event.offsetX and event.offsetY (given that you have attached the mousemove event handler to the canvas element).
Try this:
function mouseMove(e) {
var mouseX = e.offsetX;
var mouseY = e.offsetY;
}
How to get the mouse position without events (without moving the mouse)?
Real answer: No, it's not possible.
OK, I have just thought of a way. Overlay your page with a div that covers the whole document. Inside that, create (say) 2,000 x 2,000 <a>
elements (so that the :hover
pseudo-class will work in IE 6, see), each 1 pixel in size. Create a CSS :hover
rule for those <a>
elements that changes a property (let's say font-family
). In your load handler, cycle through each of the 4 million <a>
elements, checking currentStyle
/ getComputedStyle()
until you find the one with the hover font. Extrapolate back from this element to get the co-ordinates within the document.
N.B. DON'T DO THIS.
How to get mouse X position of a mouse event of a div (and not of its children) with React 17?
This works as expected in react
version 17.0.2
function Component() {
const onMouseMove = (e) => {
let rect = e.currentTarget.getBoundingClientRect();
let x = e.clientX - rect.left;
let y = e.clientY - rect.top;
console.log(x, y);
};
return (
<div onMouseMove={onMouseMove}>
<div>A</div>
<div>B</div>
<div>C</div>
</div>
);
}
Based on this answer and a comment on it, about using a currentTarget
Find mouse position relative to element
For people using JQuery:
Sometimes, when you have nested elements, one of them with the event attached to it, it can be confusing to understand what your browser sees as the parent. Here, you can specify which parent.
You take the mouse position, and then subtract it from the parent element's offset position.
var x = evt.pageX - $('#element').offset().left;
var y = evt.pageY - $('#element').offset().top;
If you're trying to get the mouse position on a page inside a scrolling pane:
var x = (evt.pageX - $('#element').offset().left) + self.frame.scrollLeft();
var y = (evt.pageY - $('#element').offset().top) + self.frame.scrollTop();
Or the position relative to the page:
var x = (evt.pageX - $('#element').offset().left) + $(window).scrollLeft();
var y = (evt.pageY - $('#element').offset().top) + $(window).scrollTop();
Note the following performance optimisation:
var offset = $('#element').offset();
// Then refer to
var x = evt.pageX - offset.left;
In this way, JQuery does not have to look up #element
for each line.
Update
There is a newer, JavaScript-only version in an answer by @anytimecoder -- see also browser support for getBoundingClientRect().
Real mouse position in canvas
The Simple 1:1 Scenario
For situations where the canvas element is 1:1 compared to the bitmap size, you can get the mouse positions by using this snippet:
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
Just call it from your event with the event and canvas as arguments. It returns an object with x
and y
for the mouse positions.
As the mouse position you are getting is relative to the client window you’ll have to subtract the position of the canvas element to convert it relative to the element itself.
Example of integration in your code:
// put this outside the event loop..
var canvas = document.getElementById("imgCanvas");
var context = canvas.getContext("2d");
function draw(evt) {
var pos = getMousePos(canvas, evt);
context.fillStyle = "#000000";
context.fillRect (pos.x, pos.y, 4, 4);
}
Note: borders and padding will affect position if applied directly to the canvas element so these needs to be considered via getComputedStyle()
– or apply those styles to a parent div instead.
When Element and Bitmap are of different sizes
When there is the situation of having the element at a different size than the bitmap itself, for example, the element is scaled using CSS or there is pixel-aspect ratio etc. you will have to address this.
Example:
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect(), // abs. size of element
scaleX = canvas.width / rect.width, // relationship bitmap vs. element for x
scaleY = canvas.height / rect.height; // relationship bitmap vs. element for y
return {
x: (evt.clientX - rect.left) * scaleX, // scale mouse coordinates after they have
y: (evt.clientY - rect.top) * scaleY // been adjusted to be relative to element
}
}
With transformations applied to context (scale, rotation etc.)
Then there is the more complicated case where you have applied transformation to the context such as rotation, skew/shear, scale, translate etc. To deal with this you can calculate the inverse matrix of the current matrix.
Newer browsers let you read the current matrix via the currentTransform
property and Firefox (current alpha) even provide an inverted matrix through the mozCurrentTransformInverted
. Firefox however, via mozCurrentTransform
, will return an Array and not DOMMatrix
as it should. Neither Chrome, when enabled via experimental flags, will return a DOMMatrix
but a SVGMatrix
.
In most cases however you will have to implement a custom matrix solution of your own (such as my own solution here – free/MIT project) until this get full support.
When you eventually have obtained the matrix regardless of path you take to obtain one, you’ll need to invert it and apply it to your mouse coordinates. The coordinates are then passed to the canvas which will use its matrix to convert it to back wherever it is at the moment.
This way the point will be in the correct position relative to the mouse. Also here you need to adjust the coordinates (before applying the inverse matrix to them) to be relative to the element.
An example just showing the matrix steps:
function draw(evt) {
var pos = getMousePos(canvas, evt); // get adjusted coordinates as above
var imatrix = matrix.inverse(); // get inverted matrix somehow
pos = imatrix.applyToPoint(pos.x, pos.y); // apply to adjusted coordinate
context.fillStyle = "#000000";
context.fillRect(pos.x-1, pos.y-1, 2, 2);
}
An example of using currentTransform
when implemented would be:
var pos = getMousePos(canvas, e); // get adjusted coordinates as above
var matrix = ctx.currentTransform; // W3C (future)
var imatrix = matrix.invertSelf(); // invert
// apply to point:
var x = pos.x * imatrix.a + pos.y * imatrix.c + imatrix.e;
var y = pos.x * imatrix.b + pos.y * imatrix.d + imatrix.f;
Update: I made a free solution (MIT) to embed all these steps into a single easy-to-use object that can be found here and also takes care of a few other nitty-gritty things most ignore.
how to get live mouse position while holding mousebutton
maybe you can try this?
let start = null
let end = null
let move = null
let isHold = false
document.addEventListener('mousedown', e => {
isHold = true
start = e
})
document.addEventListener('mouseup', e => {
isHold = false
end = e
console.log({start, end, move})
})
document.addEventListener('mousemove', e => {
if (isHold) {
move = e
}
})
Related Topics
How to Get the Coordinates of a Mouse Click on a Canvas Element
Waiting For More Than One Concurrent Await Operation
How to Write a Countdown Timer in JavaScript
How to Chain and Share Prior Results With Promises
Modify a CSS Rule Object with JavaScript
Can Jquery Change CSS Style Definition? (Not Individual CSS of Each Element)
How to Check If CSS Value Is Supported by the Browser
Reading Non-Inline CSS Style Info from JavaScript
Simulating Linux Terminal in Browser
Detect the Internet Connection Is Offline
Managing Jquery Plugin Dependency in Webpack
For Loop For Htmlcollection Elements
Difference Between Variable Declaration Syntaxes in JavaScript (Including Global Variables)
Is There a Case Insensitive Jquery :Contains Selector
How to Access the Value of Invalid/Custom CSS Properties from JavaScript