How to get the mouse position without events (without moving the mouse)?
Asked Answered
T

15

379

Is it possible to get the mouse position with JavaScript after page loads without any mouse movement event (without moving the mouse)?

Tiger answered 8/4, 2010 at 15:17 Comment(8)
Nothing wrong with the mousemove event. Just in some cases users don't move the mouse. Thanks for your answer.Tiger
Norbert Tamas, @SuperNova's answer (which wasn't added until this year) shows that mouseenter works fine for this because it fires on page load (if the mouse is in the viewport). Did it not work that way in 2010, or is it just that no one thought to try it?Sleuthhound
@CrescentFresh In some cases (like userscripts) you don't want to slow down the browser by adding many mousemove events.Stargell
Possible in FF with mouseover, but isn't in IE and Chrome.Pears
Or, in a game, your camera moves around the game world and the character is looking at the mouse (typical top-down shooter style) but if user doesn't move a mouse, it centers around the wrong point as you move around if you rely only on mousemove. However, it's not a big deal, we just store the "world" coords of the pointer and let people query that.Pigeonwing
To improve performance of this solution * Create an element with 1px height and 100% width and add :hover pseudo-class * Scan from the top of the document one pixel at a time to find Y position * After getting Y coordinate do the scan on the X axis (this time the element can be only 1px x 1px)Staghound
@Staghound Are you saying that, as you move the element, as soon as it is underneath the mouse that it can trigger an event? In the following jsfiddle, leave the mouse under the black bar and wait for the black bar to go underneath without moving, and see the mouseover and mouseenter events are not firing. jsfiddle.net/bowp569a.Fungiform
@Staghound Here's the same effort using the :hover pseudo selector, but it doesn't work without actually moving the mouse: jsfiddle.net/z9w20f53Fungiform
H
402

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.

Hilar answered 8/4, 2010 at 15:20 Comment(15)
ha ha - at some point you should google around and see if you can figure out how many people have actually implemented thisForerunner
Actually, it is implementable without having to much CPU load (I think. I haven't been testing it). On dom ready build the <a> elements with javascript, take the mouse postion and then remove all <a> elements. On mousemouse you should have other function to take the mouse position. Anyway, this was hilarious.Diabolism
Perhaps this could be made practical with binary search? Loop making a pair of <a> elements covering given rectangles (using absolute positioning of sized <img> elements, I suppose), shrinking the rectangles each time. Yes, it's ridiculous, but so is not being able to get this info before the first mousemove.Tetrastich
https://mcmap.net/q/88226/-how-to-track-mouse-position-from-on-page-load-as-well-as-on-mouse-move says the hover doesn't fire either until the mouse moves for the first time. This foils this scheme.Tetrastich
@DariusBacon: That linked answer doesn't seem to be correct: jsbin.com/utocax/3. So yes, this approach may be practical for some situations.Hilar
@TimDown a small part of me hoped to see 4 mil <a> elements while the jsbin example page was loading :DForworn
You could implement this much more efficiently by using divide-and-conquer, e.g. a binary search. You only need two <a> elements then.Ogg
I think that I can just wait until the mouse moves once and not set the CPU on fire.Actinium
Although this funny solution sounds like something that is ridiculous but working, it's not actually a working solution. It has the same problem which was there in the first place: hover events do not occur when the mouse doesn't move. You can cover your page in "hoverable" elements all you want, and they still won't be hovered upon page refresh until the mouse moves. But then you could simply respond to the mousemove event.Rhinestone
@timetowonder: I'm not sure that's always been entirely true in all browsers and operating systems. I'm pretty sure I had a simple example working in at least one browser at one time.Hilar
I don't see why this gets upvoted at all, when there's a real answer in this same topic.Toon
@Toon When I wrote this, there wasn't a real answer and if the "real answer" works then browser behaviour has changed since then. However, I do agree that this shouldn't be the accepted answer.Hilar
This is kind of amazing as it's a clear edge case for SOF. On one hand you have a clearly thoughtful answer that feasibly worked at the time of writing, which [outdated answers] are part of the nature of the historical data in general. On the other hand this "solution" is a total hack while fully encompassing the purest spirit of ingenuity and ingenuity is core to SOF! I want to see answers like this stay and I second (or third at this point) that this needn't be the accepted answer.Turbofan
@Forerunner maybe you should share one of those links for people who did google around and found this question at the top of their search resultsMulloy
@Mulloy this is essentially an impractical joke answer to the question, and it's from 11 years ago.Forerunner
M
139

Edit 2020: This does not work any more. It seems so, that the browser vendors patched this out. Because the most browsers rely on chromium, it might be in its core.

Old answer: You can also hook mouseenter (this event is fired after page reload, when the mousecursor is inside the page). Extending Corrupted's code should do the trick:

var x = null;
var y = null;
    
document.addEventListener('mousemove', onMouseUpdate, false);
document.addEventListener('mouseenter', onMouseUpdate, false);
    
function onMouseUpdate(e) {
  x = e.pageX;
  y = e.pageY;
  console.log(x, y);
}

function getMouseX() {
  return x;
}

function getMouseY() {
  return y;
}

You can also set x and y to null on mouseleave-event. So you can check if the user is on your page with it's cursor.

Mulct answered 10/4, 2014 at 11:33 Comment(10)
This would seem to be the only truly useful answer here, which seems odd. Indeed (in latest Firefox, Chrome and IE11) the mouseenter fires on page load and provides the correct coordinates. Has browser behaviour in this area simply changed in the last few years?Sleuthhound
In fact "mouseenter" doesn't seem to add any value. I tested with the following jsfiddle in Chrome and IE, and they don't show the cordinates until you put the mouse on the inner document (the result panel): jsfiddle.net/xkpd784o/1Maciemaciel
@Proton: Move your mouse to the result panel to the area of the resultpanel BEFORE page has fully loaded and don't move than. After onload the page immediatly knows the position of the mouse. No mouse movement is needed. So mouseenter is also fired, when the page has loaded and the mouse is inside the document area. That is, what the OP originally wanted. No one other provides this answer.Mulct
A potentially useful addition is to add a function for the mouseleave event that sets x and y back to null or 'undefined'Chicane
@brillout.com I've just tested it in Chrome 57 under windows: Works like a charm. What did not work in your test?Mulct
chrome 68, using the jsfiddel above, alert occurs on first mouse move and not on load, even if mouse is moved to the rendered region before the page finished loading.Chart
It seems to be a problem with the fiddle (now) or the used iframes on jsfiddle. Putting the code into a blank html file running local or via http is working like a charm for me (chrome 68)Mulct
Doesn't work. I just tested on a blank page in Chrome, Firefox and Edge. Neither mouseenter nor mousemove fire on page load even if the cursor is inside the page. See my answer for a better solution: https://mcmap.net/q/87036/-how-to-get-the-mouse-position-without-events-without-moving-the-mouseMcminn
As above, I've done various tests in Firefox and Chrome and this doesn't work on refresh, although it does work in Chrome after refresh on mouse move, but that's not what we want.Primary
The browser vendors seem to have this patched out. My answer is from 2014 and it worked several years.Mulct
C
93

What you can do is create variables for the x and y coordinates of your cursor, update them whenever the mouse moves and call a function on an interval to do what you need with the stored position.

The downside to this of course is that at least one initial movement of the mouse is required to have it work. As long as the cursor updates its position at least once, we are able to find its position regardless of whether it moves again.

var cursor_x = -1;
var cursor_y = -1;
document.onmousemove = function(event)
{
 cursor_x = event.pageX;
 cursor_y = event.pageY;
}
setInterval(check_cursor, 1000);
function check_cursor(){console.log('Cursor at: '+cursor_x+', '+cursor_y);}

The preceding code updates once a second with a message of where your cursor is.

Catechize answered 24/1, 2012 at 19:49 Comment(6)
Did you read the subject of this post? The OP asks how to get the mouse coordinates without using an event. Yet your post suggests using the onmousemove event.Chrisman
@Chrisman Although the OP specifically asked for a non-event method, this answer benefits others who came here looking for an answer and possibly a workaround. Also, I'd consider this answer partially within topic since as far as I know, this is the best method to get the cursor position at any given time without having to use events directly. That said, the answer could've been worded more along the lines of stating the fact and offering a way around to avoid nitpicking in comments.Perceive
@Pichan It didn't benefit me, because I've been looking for a way to fill those cursorX/Y variable before any event happens.Toon
very few users wont fire mouse eventsSubantarctic
Careful, it can be costly to keep an mousemove listener around. I would suggest recreating the listener in the interval and destroying the listener after you get coordinates.Overreach
any links to back up @Overreach claim?Akan
D
19

@Tim Down's answer is not performant if you render 2,000 x 2,000 <a> elements:

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 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 elements that changes a property (let's say font-family). In your load handler, cycle through each of the 4 million 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.

But you don't have to render 4 million elements at once, instead use binary search. Just use 4 <a> elements instead:

  • Step 1: Consider the whole screen as the starting search area
  • Step 2: Split the search area into 2 x 2 = 4 rectangle <a> elements
  • Step 3: Using the getComputedStyle() function determine in which rectangle mouse hovers
  • Step 4: Reduce the search area to that rectangle and repeat from step 2.

This way you would need to repeat these steps max 11 times, considering your screen is not wider than 2048px.

So you will generate max 11 x 4 = 44 <a> elements.

If you don't need to determine the mouse position exactly to a pixel, but say 10px precision is OK. You would repeat the steps at most 8 times, so you would need to draw max 8 x 4 = 32 <a> elements.

Also generating and then destroying the <a> elements is not performat as DOM is generally slow. Instead, you can just reuse the initial 4 <a> elements and just adjust their top, left, width and height as you loop through steps.

Now, creating 4 <a> is an overkill as well. Instead, you can reuse the same one <a> element for when testing for getComputedStyle() in each rectangle. So, instead of splitting the search area into 2 x 2 <a> elements just reuse a single <a> element by moving it with top and left style properties.

So, all you need is a single <a> element change its width and height max 11 times, and change its top and left max 44 times and you will have the exact mouse position.

Disentwine answered 10/4, 2017 at 14:48 Comment(2)
Clever! This is the only answer to actually "answer" the question in a performant way. Has anyone come up with a proof of concept fiddle?Lightface
Here is a decent attempt! jsfiddle.net/aqn1yo70/158 I was having trouble with hover states when using just one <a> element, and then you must wait for the browser to render the elements in a hovered state, which takes one browser cycle. Browsers usually run around 60 FPS, so to make the fiddle more precise, change await sleep(50) to 17, bc 1/60 is about 17ms. And if you want to enjoy the visualization more thoroughly, set that number to ~500Bruyn
H
11

You could try something similar to what Tim Down suggested - but instead of having elements for each pixel on the screen, create just 2-4 elements (boxes), and change their location, width, height dynamically to divide the yet possible locations on screen by 2-4 recursively, thus finding the mouse real location quickly.

For example - first elements take right and left half of screen, afterwards the upper and lower half. By now we already know in which quarter of screen the mouse is located, are able to repeat - discover which quarter of this space...

Hasheem answered 11/2, 2015 at 8:46 Comment(0)
M
9

Here's my solution. It exports window.currentMouseX and window.currentMouseY properties you can use anywhere. It uses the position of a hovered element (if any) initially and afterwards listens to mouse movements to set the correct values.

(function () {
    window.currentMouseX = 0;
    window.currentMouseY = 0;

    // Guess the initial mouse position approximately if possible:
    var hoveredElement = document.querySelectorAll(':hover');
    hoveredElement = hoveredElement[hoveredElement.length - 1]; // Get the most specific hovered element

    if (hoveredElement != null) {
        var rect = hoveredElement.getBoundingClientRect();
        // Set the values from hovered element's position
        window.currentMouseX = window.scrollX + rect.x;
        window.currentMouseY = window.scrollY + rect.y;
    }

    // Listen for mouse movements to set the correct values
    window.addEventListener('mousemove', function (e) {
        window.currentMouseX = e.pageX;
        window.currentMouseY = e.pageY;
    }, /*useCapture=*/true);
}())

Composr CMS Source: https://github.com/ocproducts/composr/commit/a851c19f925be20bc16bfe016be42924989f262e#diff-b162dc9c35a97618a96748639ff41251R1202

Mcminn answered 2/9, 2018 at 21:8 Comment(0)
T
6

The most simple solution but not 100% accurate

$(':hover').last().offset()

Result: {top: 148, left: 62.5}
The result depend on the nearest element size and return undefined when user switched the tab

Tapes answered 10/1, 2018 at 12:8 Comment(4)
For me, it returns undefined regardless. Can you elaborate as how to use this?Bettyebettzel
It would return undefined when the cursor wasn't hovering any element (or when the browser lost focus). You may need to set a time interval if you're testing from the console..Tapes
Thanks. setTimeout worked. I was using jsfiddle and you're right, it never hit a hover event because it redraws the DOM each time you click play. I would recommend adding this hint for others.Bettyebettzel
I do not want accurate mouse position but i just want to know that mouse is extreme right or extreme left into function without event object so your solution works in my case..thank youLamond
P
5

Yes, It's possible.

If you add "mouseover" event to the document it will fire instantly and you can get the mouse position, of course if mouse pointer was over the document.

   document.addEventListener('mouseover', setInitialMousePos, false);

   function setInitialMousePos( event ) {
       console.log( event.clientX, event.clientY);
       document.removeEventListener('mouseover', setInitialMousePos, false);
   }

Previously it was possible to read mouse position through window.event but it's deprecated now.

Pastorship answered 27/8, 2021 at 1:1 Comment(1)
This is not working anymore. Tested with Chrome 107.Materialist
G
2

Riffing on @SuperNova's answer, here's an approach using ES6 classes that keeps the context for this correct in your callback:

class Mouse {
  constructor() {
    this.x = 0;
    this.y = 0;
    this.callbacks = {
      mouseenter: [],
      mousemove: [],
    };
  }

  get xPos() {
    return this.x;
  }

  get yPos() {
    return this.y;
  }

  get position() {
    return `${this.x},${this.y}`;
  }

  addListener(type, callback) {
    document.addEventListener(type, this); // Pass `this` as the second arg to keep the context correct
    this.callbacks[type].push(callback);
  }

  // `handleEvent` is part of the browser's `EventListener` API.
  // https://developer.mozilla.org/en-US/docs/Web/API/EventListener/handleEvent
  handleEvent(event) {
    const isMousemove = event.type === 'mousemove';
    const isMouseenter = event.type === 'mouseenter';

    if (isMousemove || isMouseenter) {
      this.x = event.pageX;
      this.y = event.pageY;
    }

    this.callbacks[event.type].forEach((callback) => {
      callback();
    });
  }
}

const mouse = new Mouse();

mouse.addListener('mouseenter', () => console.log('mouseenter', mouse.position));
mouse.addListener('mousemove', () => console.log('mousemove A', mouse.position));
mouse.addListener('mousemove', () => console.log('mousemove B', mouse.position));
Gerry answered 2/2, 2018 at 6:16 Comment(0)
S
1
var x = 0;
var y = 0;

document.addEventListener('mousemove', onMouseMove, false)

function onMouseMove(e){
    x = e.clientX;
    y = e.clientY;
}

function getMouseX() {
    return x;
}

function getMouseY() {
    return y;
}
Spires answered 9/11, 2012 at 14:31 Comment(2)
Doesn't this still require the user to move the mouse?Sanmiguel
Yes but only the first move. Then when its moved once we know already the px X YAva
Y
1

I implemented a horizontal/vertical search, (first make a div full of vertical line links arranged horizontally, then make a div full of horizontal line links arranged vertically, and simply see which one has the hover state) like Tim Down's idea above, and it works pretty fast. Sadly, does not work on Chrome 32 on KDE.

jsfiddle.net/5XzeE/4/

Ynez answered 6/11, 2013 at 1:36 Comment(1)
apparently these tricks no longer work unless there is an explicit mouse move by the user. :(Fungiform
G
1

You do not have to move the mouse to get the cursor's location. The location is also reported on events other than mousemove. Here's a click-event as an example:

document.body.addEventListener('click',function(e)
{
    console.log("cursor-location: " + e.clientX + ',' + e.clientY);
});
Grote answered 28/6, 2017 at 17:23 Comment(0)
A
0

I envision that maybe you have a parent page with a timer and after a certain amount of time or a task is completed, you forward the user to a new page. Now you want the cursor position, and because they are waiting, they aren't necessarily touching the mouse. So track the mouse on the parent page using standard events and pass the last value to the new page in a get or a post variable.

You can use JHarding's code on your parent page so that the latest position is always available in a global variable:

var cursorX;
var cursorY;
document.onmousemove = function(e){
    cursorX = e.pageX;
    cursorY = e.pageY;
}

This won't help users that navigate to this page by means other than your parent page.

Adulterer answered 17/10, 2013 at 19:59 Comment(0)
I
0

Not mouse position, but, if you're looking for current cursor postion (for use cases like getting last typed character etc) then, below snippet works fine.
This will give you the cursor index related to text content.

window.getSelection().getRangeAt(0).startOffset
Infielder answered 11/12, 2020 at 13:5 Comment(0)
E
-1

I think i may have a reasonable solution with out counting divs and pixels..lol

Simply use animation frame or a time interval of a function. you will still need a mouse event one time though just to initiate, but technically you position this where ever you like.

Essentially we are tracking a dummy div at all times with out mouse movement.

// create a div(#mydiv) 1px by 1px set opacity to 0 & position:absolute;

Below is the logic..

var x,y;


$('body').mousemove(function( e ) {

    var x = e.clientX - (window.innerWidth / 2);
    var y = e.clientY - (window.innerHeight / 2);
 }


function looping (){

   /* track my div position 60 x 60 seconds!
      with out the mouse after initiation you can still track the dummy div.x & y
      mouse doesn't need to move.*/

   $('#mydiv').x = x;    // css transform x and y to follow 
   $('#mydiv)'.y = y;

   console.log(#mydiv.x etc)

   requestAnimationFrame( looping , frame speed here);
}  
Eyewitness answered 18/1, 2019 at 12:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.