379

I want to make a little painting app using canvas. So I need to find the mouse's position on the canvas.

Ben Shelock
  • 20,154
  • 26
  • 92
  • 125

30 Answers30

633

As I didn't find a jQuery-free answer that I could copy/paste, here's the solution I used:

document.getElementById('clickme').onclick = function(e) {
      // e = Mouse click event.
      var rect = e.target.getBoundingClientRect();
      var x = e.clientX - rect.left; //x position within the element.
      var y = e.clientY - rect.top;  //y position within the element.
      console.log("Left? : " + x + " ; Top? : " + y + ".");
    }
#clickme {
  margin-top: 20px;
  margin-left: 100px;
  border: 1px solid black;
  cursor: pointer;
}
<div id="clickme">Click Me -<br>
(this box has margin-left: 100px; margin-top: 20px;)</div>

JSFiddle of full example

Danny Ruijters
  • 3,411
  • 2
  • 18
  • 21
anytimecoder
  • 7,204
  • 2
  • 14
  • 18
  • 6
    You need to [round the bounding rect](https://stackoverflow.com/questions/40879171/in-javascript-why-does-getboundingclientrect-sometimes-return-floating-point) to ensure integer values with this method. – Jake Cobb Jun 20 '17 at 19:57
  • 4
    New edit should fix scrolling issues (using clientX/Y) – Erik Koopmans Feb 21 '18 at 11:12
  • 6
    This answer should be voted up. Thank you anytimecoder and @ErikKoopmans. – Ivan Jun 01 '18 at 21:19
  • This is the only answer that works in all the cases I have tested. Plus, it is simple and efficient! (Answers based on offsetLeft/offsetTop fail when there is a margin applied to the document.documentElement.) – Robin Stewart Sep 10 '18 at 20:26
  • 3
    PSA: @mpen 's comment is obsolete; this method DOES work, even in cases with scrolling. See updated version of their Fiddle: https://jsfiddle.net/6wcsovp2/ – Xharlie Mar 24 '19 at 18:48
  • 20
    Thank you! One note: you need `e.currentTarget`, not e.target. Otherwise, children elements will affect it – Maxím G. Sep 03 '19 at 12:45
  • 1
    true @MaximGeorgievskiy – Martin Cremer Dec 09 '19 at 15:24
  • You can check for older browser compatibility here: https://caniuse.com/#feat=getboundingclientrect – sparkyspider Mar 07 '20 at 12:46
  • If the page is scaled, however, this is not working.... Also if the canvas is scaled, the values will be wrong. Any solution for this case? – Xerix Oct 09 '20 at 01:37
  • 1
    The above code returns the top left of the target. To get the center, shift the mouse position by also subtracting half the size of the target: ```const rect = target.getBoundingClientRect(); const xPosition = clientX - rect.left - rect.width / 2; const yPosition = clientY - rect.top - rect.height / 2;``` – s4l4x Nov 26 '20 at 01:22
  • 3
    Thank you. I find it obnoxious when someone expects you to import all of jQuery just to use one small feature that could easily be implemented without it. Especially since jQuery is rather poorly designed in my opinion. – Braden Best Mar 02 '21 at 16:38
  • @sparkyspider "You can check for older browser compatibility here:" please do not encourage the use of old browsers. Web browsers already introduce a massive security hole into any system they're installed on; using an outdated version of one is just asking for trouble. – Braden Best Mar 02 '21 at 16:43
  • @Xerix: If your canvas is scaled, you can convert the coordinates from screen coordinates to canvas coordinates by doing `x = x * canvas.width / canvas.clientWidth;` and the same to `y`. This probably only works if you have no padding. – Iziminza Sep 10 '22 at 17:13
203

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().

Jeff Ward
  • 16,563
  • 6
  • 48
  • 57
sparkyspider
  • 13,195
  • 10
  • 89
  • 133
  • 7
    Did it work for you @ben, I'd appreciate a "Correct Answer" or a comment on what's still not working for you. Maybe I can help further. – sparkyspider Sep 27 '12 at 21:28
  • 9
    Should avoid using $('selector') in an event handler, unless you want very sluggish code. Pre-select your element, and use the preselected reference in your handler. Same for $(window) do that once and cache it. – Austin France Oct 11 '13 at 16:35
  • 2
    this isn't a necessary mention: if you are copy pasting the above code, remember to correct `var y = (evt.pageY - $('#element').offset().left) + $(window).scrollTop();` to `var y = (evt.pageY - $('#element').offset().top) + $(window).scrollTop();` – Raj Nathani Jan 01 '14 at 21:02
  • 126
    How could that be THE answer since the question has nothing to do with jQuery? – FlorianB Aug 30 '16 at 12:57
  • @FlorianB the question has something to do with javascript and jQuery is obviously a javascript framework... – Black Aug 07 '17 at 20:37
  • 4
    @Black but some people ask things in javascript instead of jquery because jquery may not be compatible in their environments. – dbrree Aug 22 '17 at 16:20
  • Dont know at the time of the answer but now jQuery is not needed. I just do `let x = evt.pageX - evt.target.offsetLeft; let y = ev.pageY - ev.target.offsetTop;` – May.D Feb 29 '20 at 16:35
86

The following calculates the mouse position relation to the canvas element:

const example = document.getElementById('example');

example.onmousemove = function(e) { 
    const x = e.pageX - e.currentTarget.offsetLeft; 
    const y = e.pageY - e.currentTarget.offsetTop; 
}
knownasilya
  • 5,998
  • 4
  • 36
  • 59
LindaQ
  • 1,059
  • 7
  • 4
  • 87
    Maybe it's me but two lines of codes that use the keyword "this" lack context? – Deratrius Sep 24 '12 at 13:45
  • 10
    Definitely lacking context. 'e' is the event and 'this' is the element the event is tied to `var example = document.getElementById('example'); example.onmousemove = function(e) { var X = e.pageX - this.offsetLeft; var Y = e.pageY - this.offsetTop; }` – Nick Bisby May 20 '13 at 22:17
  • `this.offsetLeft` doesn't work right in my Firefox 24.0a2 (2013-08-05). `$(this).offset().left` does. – Cees Timmerman Aug 09 '13 at 17:12
  • 27
    This doesn't work if some descendent element uses relative or absolute positioning. – edA-qa mort-ora-y Dec 15 '13 at 16:49
  • 12
    You can switch out `this` to `e.currentTarget`, that will get you the position relative to the element that you added the event listener to. – Linus Unnebäck Jul 31 '15 at 07:19
  • 4
    This seems to give the position relatve to the *visible* part of the element, not the whole element. Is there a way to accommodate for that if part of the element cannot be seen in the viewport and there are scrollbars, etc.? – frabjous Jan 28 '18 at 16:25
  • 1
    **ATTENTION:** [`element.offsetLeft`](https://dev.mozilla.org/Web/API/HTMLElement/offsetLeft "MDN Web Docs") returns only the distance to **nearest parent** element. To compute distance to whole viewport please use [`element.getClientRects().left`](https://dev.mozilla.org/Web/API/Element/getClientRects "MDN Web Docs") –  Jul 09 '21 at 16:13
  • with flexbox etc these solutions become less and less useful – LuckyLuke Skywalker Jun 01 '23 at 10:29
47

There is no answer in pure javascript that returns relative coordinates when the reference element is nested inside others which can be with absolute positioning. Here is a solution to this scenario:

function getRelativeCoordinates (event, referenceElement) {

  const position = {
    x: event.pageX,
    y: event.pageY
  };

  const offset = {
    left: referenceElement.offsetLeft,
    top: referenceElement.offsetTop
  };

  let reference = referenceElement.offsetParent;

  while(reference){
    offset.left += reference.offsetLeft;
    offset.top += reference.offsetTop;
    reference = reference.offsetParent;
  }

  return { 
    x: position.x - offset.left,
    y: position.y - offset.top,
  }; 

}
Patrick Boos
  • 6,789
  • 3
  • 35
  • 36
Mouloud85
  • 3,826
  • 5
  • 22
  • 42
  • 3
    I don't think it takes into account `html` element offset – apieceofbart Oct 21 '16 at 08:42
  • 4
    Works great with scrolling containers, with just a little change: replace `while(reference !== undefined)` with `while(reference)`. In Chrome at least, the offsetParent does not end up being `undefined`, but `null`. Just checking whether `reference` is truthy will ensure it works with both `undefined` and `null`. – blex Jan 07 '19 at 11:10
  • In general, checking against undefined is usually a bad idea – Ruan Mendes Jun 04 '19 at 22:17
  • I think you need to subtract `scrollLeft` and `scrollTop` to compensate scrolled descendants. – Robert Apr 11 '20 at 21:54
  • Also doesn't account for padding, which isn't accounted for by offset values. E.g. `offset.left += reference.offsetLeft - reference.scrollLeft + parseInt(getComputedStyle(reference).paddingLeft); `, and likewise for top. – Andrew Feb 17 '22 at 01:40
39

I tried all these solutions and due to my special setup with a matrix transformed container (panzoom library) none worked. This returns the correct value, even if zoomed and paned:

mouseevent(e) {
 const x = e.offsetX,
       y = e.offsetY
}

But only if there are no child elements in the way. This can be circumvented by making them 'invisible' to the event, using CSS:

.child {
   pointer-events: none;
}
Fabian von Ellerts
  • 4,763
  • 40
  • 35
  • 4
    This is definitely the way to do this in 2019. The other answers are all loaded with old crufty baggage. – Colin D Apr 08 '19 at 16:07
  • 1
    @ColinD thank you! I forgot to add that this also works in IE11. – Fabian von Ellerts Apr 09 '19 at 08:07
  • This worked for me. However, I'm suspicious that 1) Few upvotes 2) higher rated answers are way more complicated and 3) I see no drawbacks explained. Does anyone know any con of using this approach? – Mark E Feb 07 '20 at 17:43
  • 1
    @MarkE There's nothing suspicious about it. It's one of the many faults of StackOverflow. Those answer you see at the top were posted many years before this answer here, so they gained tremendous up-vote momentum. Once an answer gains this momentum, it's pretty hard and pretty rare to beat it. – Jack G Mar 02 '20 at 22:02
  • 2
    @MarkE The answer is simple: [it's experimental](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/offsetX). Some might care, others might not, but it's a fact. – cubuspl42 Apr 30 '21 at 14:14
  • Another potential reason is that it's the offset of the _target_ node, which is not necessarily the node that the event listener is attached to. It may be the desired behavior, but it may be not. – cubuspl42 Apr 30 '21 at 14:50
  • Do you know how to get rid of this problem regarding child elements in the way? – Marcin Żmigrodzki Jan 08 '23 at 12:32
  • MDN calls everything "experimental" if they haven't implemented it, even if they're the only ones. It's not very meaningful. – Glenn Maynard May 25 '23 at 05:01
22

A good write up of the difficulty of this problem can be found here: http://www.quirksmode.org/js/events_properties.html#position

Using the technique that is described there you can find the mouses position in the document. Then you just check to see if it is inside the bounding box of your element, which you can find by calling element.getBoundingClientRect() which will return an object with the following properties: { bottom, height, left, right, top, width }. From there it is trivial to figure out if the even happened inside your element or not.

pid
  • 11,472
  • 6
  • 34
  • 63
David W. Keith
  • 2,246
  • 17
  • 20
11

I came across this question, but in order to make it work for my case (using dragover on a DOM-element (not being canvas in my case)), I found that you only have have to use offsetX and offsetY on the dragover-mouse event.

onDragOver(event){
 var x = event.offsetX;
 var y = event.offsetY;
}
John
  • 10,165
  • 5
  • 55
  • 71
  • 14
    event.offsetX is relative to the element that the mouse pointer is currently over, not the element that has the dragover listener attached to it. If you don't have a nested element structure then this is fine, but if you do then this isn't going to work. – opsb Aug 05 '18 at 10:24
11

If you want to get the layerX and layerY related to one element, maybe you could try:

let bbox_rect = document.getElementById("dom-ID").getBoundingClientRect()
let layerX = e.clientX-bbox_rect.left
let layerY = e.clientY-bbox_rect.top
Kohill Yang
  • 111
  • 1
  • 3
9

I +1' Mark van Wyk's answer as it got me in the right direction, but didn't quite solve it for me. I still had an offset on painting in elements contained within another element.

FOllowing solved it for me:

        x = e.pageX - this.offsetLeft - $(elem).offset().left;
        y = e.pageY - this.offsetTop - $(elem).offset().top;

In other words - i simply stacked all the offsets from all elements nested

Matt
  • 278
  • 3
  • 12
  • +1 for you! This is the answer I've been looking for. I had two canvases stacked on top of each other and the coordinates were all wrong because of the other divs. I knew this was the problem, but didn't know the logic/equation to compensate it. You just fixed my problem too! =D thanks! – Partack Jun 08 '13 at 09:30
7

For those of you developing regular websites or PWAs (Progressive Web Apps) for mobile devices and/or laptops/monitors with touch screens, then you have landed here because you might be used to mouse events and are new to the sometimes painful experience of Touch events... yay!

There are just 3 rules:

  1. Do as little as possible during mousemove or touchmove events.
  2. Do as much as possible during mousedown or touchstart events.
  3. Cancel propagation and prevent defaults for touch events to prevent mouse events from also firing on hybrid devices.

Needless to say, things are more complicated with touch events because there can be more than one and they're more flexible (complicated) than mouse events. I'm only going to cover a single touch here. Yes, I'm being lazy, but it's the most common type of touch, so there.

var posTop;
var posLeft;
function handleMouseDown(evt) {
  var e = evt || window.event; // Because Firefox, etc.
  posTop = e.target.offsetTop;
  posLeft = e.target.offsetLeft;
  e.target.style.background = "red";
  // The statement above would be better handled by CSS
  // but it's just an example of a generic visible indicator.
}
function handleMouseMove(evt) {
  var e = evt || window.event;
  var x = e.offsetX; // Wonderfully
  var y = e.offsetY; // Simple!
  e.target.innerHTML = "Mouse: " + x + ", " + y;
  if (posTop)
    e.target.innerHTML += "<br>" + (x + posLeft) + ", " + (y + posTop);
}
function handleMouseOut(evt) {
  var e = evt || window.event;
  e.target.innerHTML = "";
}
function handleMouseUp(evt) {
  var e = evt || window.event;
  e.target.style.background = "yellow";
}
function handleTouchStart(evt) {
  var e = evt || window.event;
  var rect = e.target.getBoundingClientRect();
  posTop = rect.top;
  posLeft = rect.left;
  e.target.style.background = "green";
  e.preventDefault(); // Unnecessary if using Vue.js
  e.stopPropagation(); // Same deal here
}
function handleTouchMove(evt) {
  var e = evt || window.event;
  var pageX = e.touches[0].clientX; // Touches are page-relative
  var pageY = e.touches[0].clientY; // not target-relative
  var x = pageX - posLeft;
  var y = pageY - posTop;
  e.target.innerHTML = "Touch: " + x + ", " + y;
  e.target.innerHTML += "<br>" + pageX + ", " + pageY;
  e.preventDefault();
  e.stopPropagation();
}
function handleTouchEnd(evt) {
  var e = evt || window.event;
  e.target.style.background = "yellow";
  // Yes, I'm being lazy and doing the same as mouseout here
  // but obviously you could do something different if needed.
  e.preventDefault();
  e.stopPropagation();
}
div {
  background: yellow;
  height: 100px;
  left: 50px;
  position: absolute;
  top: 80px;
  user-select: none; /* Disable text selection */
  -ms-user-select: none;
  width: 100px;
}
<div 
  onmousedown="handleMouseDown()" 
  onmousemove="handleMouseMove()"
  onmouseout="handleMouseOut()"
  onmouseup="handleMouseUp()" 
  ontouchstart="handleTouchStart()" 
  ontouchmove="handleTouchMove()" 
  ontouchend="handleTouchEnd()">
</div>
Move over box for coordinates relative to top left of box.<br>
Hold mouse down or touch to change color.<br>
Drag to turn on coordinates relative to top left of page.

Prefer using Vue.js? I do! Then your HTML would look like this:

<div @mousedown="handleMouseDown"
     @mousemove="handleMouseMove"
     @mouseup="handleMouseUp"
     @touchstart.stop.prevent="handleTouchStart"
     @touchmove.stop.prevent="handleTouchMove"
     @touchend.stop.prevent="handleTouchEnd">
John T.
  • 451
  • 6
  • 6
5

None of the above answers are satisfactory IMO, so here's what I use:

// Cross-browser AddEventListener
function ael(e, n, h){
    if( e.addEventListener ){
        e.addEventListener(n, h, true);
    }else{
        e.attachEvent('on'+n, h);
    }
}

var touch = 'ontouchstart' in document.documentElement; // true if touch device
var mx, my; // always has current mouse position IN WINDOW

if(touch){
    ael(document, 'touchmove', function(e){var ori=e;mx=ori.changedTouches[0].pageX;my=ori.changedTouches[0].pageY} );
}else{
    ael(document, 'mousemove', function(e){mx=e.clientX;my=e.clientY} );
}

// local mouse X,Y position in element
function showLocalPos(e){
    document.title = (mx - e.getBoundingClientRect().left)
        + 'x'
        + Math.round(my - e.getBoundingClientRect().top);
}

And if you ever need to know the current Y scrolling position of page :

var yscroll = window.pageYOffset
        || (document.documentElement && document.documentElement.scrollTop)
        || document.body.scrollTop; // scroll Y position in page
FlorianB
  • 2,188
  • 18
  • 30
5

Taken from this tutorial, with corrections made thanks to the top comment:

function getMousePos( canvas, evt ) {
    var rect = canvas.getBoundingClientRect();
    return {
        x: Math.floor( ( evt.clientX - rect.left ) / ( rect.right - rect.left ) * canvas.width ),
        y: Math.floor( ( evt.clientY - rect.top ) / ( rect.bottom - rect.top ) * canvas.height )
    };
}

Use on a canvas as follows:

var canvas = document.getElementById( 'myCanvas' );
canvas.addEventListener( 'mousemove', function( evt ) {
    var mousePos = getMousePos( canvas, evt );
} );
Jonathan H
  • 7,591
  • 5
  • 47
  • 80
5
canvas.onmousedown = function(e) {
    pos_left = e.pageX - e.currentTarget.offsetLeft;
    pos_top = e.pageY - e.currentTarget.offsetTop;
    console.log(pos_left, pos_top)
}

HTMLElement.offsetLeft

The HTMLElement.offsetLeft read-only property returns the number of pixels that the upper left corner of the current element is offset to the left within the HTMLElement.offsetParent node.

For block-level elements, offsetTop, offsetLeft, offsetWidth, and offsetHeight describe the border box of an element relative to the offsetParent.

However, for inline-level elements (such as span) that can wrap from one line to the next, offsetTop and offsetLeft describe the positions of the first border box (use Element.getClientRects() to get its width and height), while offsetWidth and offsetHeight describe the dimensions of the bounding border box (use Element.getBoundingClientRect() to get its position). Therefore, a box with the left, top, width and height of offsetLeft, offsetTop, offsetWidth and offsetHeight will not be a bounding box for a span with wrapped text.

HTMLElement.offsetTop

The HTMLElement.offsetTop read-only property returns the distance of the current element relative to the top of the offsetParent node.

MouseEvent.pageX

The pageX read-only property returns the X (horizontal) coordinate in pixels of the event relative to the whole document. This property takes into account any horizontal scrolling of the page.

MouseEvent.pageY

The MouseEvent.pageY read-only property returns the Y (vertical) coordinate in pixels of the event relative to the whole document. This property takes into account any vertical scrolling of the page.

For further explanation, please see the Mozilla Developer Network:

https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageY https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetLeft https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetTop

kungfooman
  • 4,473
  • 1
  • 44
  • 33
  • While this code snippet may solve the question, [including an explanation](http://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – Patrick Hund Aug 20 '17 at 09:36
  • I really hope readers in the future have a JS API which calculates this for them. ;-) This is probably the simplest, full, self-contained answer. Commenting self-explanatory code is just distracting waste. – kungfooman Aug 20 '17 at 09:59
  • I second @PatrickHund's request for an explanation. I'm not a CSS/JS expert, and knowing more about the relationships between, e.g., `page` and `offset` would be very helpful to me. Thank you for considering this request! – cxw Sep 13 '17 at 17:17
  • @cxw Ok, you are the 2nd downvote now (had 1 upvote)... I added a long explanation now, it's like 10x as long as the 4 lines of code. You are welcome, have fun reading. I considered your request! :p – kungfooman Sep 14 '17 at 13:17
  • Thank you! That is very helpful - downvote removed. One last question: MDN says `offsetParent` is the closest enclosing positioned element. Does that mean the code will break if there is a positioned parent other than the whole document? – cxw Sep 14 '17 at 14:04
  • 1
    @cxw I messed around by adding an extra absolute positioned element and forcing the canvas element itself offscreen aswell, so I had to scroll to it, like left/top 2000px offscreen. Couldn't get this code to break. – kungfooman Sep 14 '17 at 14:32
4

const findMousePositionRelativeToElement = (e) => {
    const xClick = e.clientX - e.currentTarget.offsetLeft;
    const yClick = e.clientY - e.currentTarget.offsetTop;
    console.log(`x: ${xClick}`);
    console.log(`y: ${yClick}`);

    // or
    const rect = e.currentTarget.getBoundingClientRect();
    const xClick2 = e.clientX - rect.left;
    const yClick2 = e.clientY - rect.top;
    console.log(`x2: ${xClick2}`);
    console.log(`y2: ${yClick2}`);
}

Artem Bochkarev
  • 1,242
  • 13
  • 23
3

I realise I'm a little late , but this works with PURE javascript, and it even gives you the coordinates of the pointer within the element if the element is bigger than the viewport and the user has scrolled.

var element_offset_x ; // The distance from the left side of the element to the left of the content area


....// some code here (function declaration or element lookup )



element_offset_x = element.getBoundingClientRect().left  -  document.getElementsByTagName("html")[0].getBoundingClientRect().left  ;

....// code here 




function mouseMoveEvent(event) 
{
   var pointer_location = (event.clientX + window.pageXOffset) - element_offset_x ; 
}

How it works.

The first thing we do is get the location of the HTML element (the content area) relative to the current viewport. If the page has scrollbars and is scrolled, then the number returned by getBoundingClientRect().left for the html tag will be negative. We then use this number to compute the distance between the element and the left of the content area. With element_offset_x = element.getBoundingClientRect().left......;

Knowing the distance of the element from the content area. event.clientX gives us the distance of the pointer from the viewport. It is important to understand that the viewport and the content area are two different entities, the viewport can move if the page is scrolled. Hence, clientX will return the SAME number even if the page is scrolled.

To compensate for this , we need to add the x position of the pointer (relative to the viewport) , to the x position of the viewport (relative to the content area ). The X position of the viewport is found with window.pageXOffset.

FutureSci
  • 1,750
  • 1
  • 19
  • 27
  • 1
    Thank you for being the only answerer to think about scrolling. Consider changing your answer to add the y-axis as well. – frabjous Jan 28 '18 at 16:30
3

Based on @Spider's solution, my non JQuery version is this:

// Get the container element's bounding box
var sides = document.getElementById("container").getBoundingClientRect();

// Apply the mouse event listener
document.getElementById("canvas").onmousemove = (e) => {
  // Here 'self' is simply the current window's context
  var x = (e.clientX - sides.left) + self.pageXOffset;
  var y = (e.clientY - sides.top) + self.pageYOffset;
}

This works both with scrolling and zooming (in which case sometimes it returns floats).

Letokteren
  • 749
  • 1
  • 15
  • 28
  • I would upvote, but shouldn't those be minus signs for the page offsets? Either that, or the parentheses around the second part? – Chris Sunami Dec 27 '17 at 15:20
2

You can use getBoudingClientRect() of the relative parent.

document.addEventListener("mousemove", (e) => {
  let xCoord = e.clientX - e.target.getBoundingClientRect().left + e.offsetX
  let yCoord = e.clientY - e.target.getBoundingClientRect().top + e.offsetY
  console.log("xCoord", xCoord, "yCoord", yCoord)
})
2

Use this method to get mouse position quickly:

Object.defineProperty(MouseEvent.prototype, "mouseX", {
   get() {
      return this.clientX - this.currentTarget.getBoundingClientRect().left;
   }
});
Object.defineProperty(MouseEvent.prototype, "mouseY", {
   get() {
      return this.clientY - this.currentTarget.getBoundingClientRect().top;
   }
});

example:

document.body.onmousemove=function(e){console.log(e.mouseX,e.mouseY)}
Fei Sun
  • 361
  • 1
  • 3
  • 10
1

The mouse coordinates inside a canvas can be obtained thanks to event.offsetX and event.offsetY. Here's a little snippet to prove my point:

c=document.getElementById("c");
ctx=c.getContext("2d");
ctx.fillStyle="black";
ctx.fillRect(0,0,100,100);
c.addEventListener("mousemove",function(mouseEvt){
  // the mouse's coordinates on the canvas are just below
  x=mouseEvt.offsetX;
  y=mouseEvt.offsetY;
  // the following lines draw a red square around the mouse to prove it
  ctx.fillStyle="black";
  ctx.fillRect(0,0,100,100);
  ctx.fillStyle="red";
  ctx.fillRect(x-5,y-5,10,10);
});
  
body {
  background-color: blue;
}

canvas {
  position: absolute;
  top: 50px;
  left: 100px;
}
<canvas id="c" width="100" height="100"></canvas>
    
Nino Filiu
  • 16,660
  • 11
  • 54
  • 84
1

I implemented an other solution that I think is very simple so I thought I'd share with you guys.

So, the problem for me was that the dragged div would jump to 0,0 for the mouse cursor. So I needed to capture the mouses position on the div to adjust the divs new position.

I read the divs PageX and PageY and set the top and left of the according to that and then to get the values to adjust the coordinates to keep the cursor in the initial position in the div I use a onDragStart listener and store the e.nativeEvent.layerX and e.nativeEvent.layerY that only in the initial trigger gives you the mouses position within the draggable div.

Example code :

 onDrag={(e) => {
          let newCoords;
          newCoords = { x: e.pageX - this.state.correctionX, y: e.pageY - this.state.correctionY };
          this.props.onDrag(newCoords, e, item.id);
        }}
        onDragStart={
          (e) => {
            this.setState({
              correctionX: e.nativeEvent.layerX,
              correctionY: e.nativeEvent.layerY,
            });
          }

I hope this will help someone that went through the same problems I went through :)

1
function myFunction(e) {
    var x =  e.clientX - e.currentTarget.offsetLeft ; 
    var y = e.clientY - e.currentTarget.offsetTop ;
}

this works ok!

MbPCM
  • 457
  • 5
  • 12
1

You can simply use jQuery’s event.pageX and event.pageY with the method offset() of jQuery to get the position of the mouse relative to an element.

  $(document).ready(function() {
    $("#myDiv").mousemove(function(event){            
      var X = event.pageX - $(this).offset().left;
      var Y = event.pageY - $(this).offset().top;
      $(".cordn").text("(" + X + "," + Y + ")");
    });
  });

You can see an example here: How to find mouse position relative to element

thomas
  • 785
  • 8
  • 7
0

you can get it by

var element = document.getElementById(canvasId);
element.onmousemove = function(e) {
    var xCoor = e.clientX;
    var yCoor = e.clientY;
}
rob waminal
  • 18,117
  • 17
  • 50
  • 64
  • Will this work in IE, or does IE still use window.event instead of passing the event to the first argument of the listener? edit - I guess IE lacks the canvas element anyway until version 9, but this should still probably support IE because of things like excanvas.... – Dagg Nabbit Jul 13 '10 at 05:12
  • 29
    This will give the coordinates relative to the browser window, still need to figure out if they are in the element using element.getBoundingClientRect() – David W. Keith Jul 13 '10 at 05:13
  • thank you for making me aware of getBoundingClientRect!!!! I never realized there was such a thing! – Gershom Maes Jun 23 '14 at 17:13
  • @DavidW.Keith comment should really be considered the correct answer! – Ulrik. S Oct 27 '19 at 10:23
0

You have to know the structure of your page, because if your canvas is a child of a div which in turn is a child of another div... then the story gets more complicated. Here's my code for a canvas which is inside 2 levels of div s:

canvas.addEventListener("click", function(event) {
var x = event.pageX - (this.offsetLeft + this.parentElement.offsetLeft);
var y = event.pageY - (this.offsetTop + this.parentElement.offsetTop);
console.log("relative x=" + x, "relative y" + y);

});

Eli Mashiah
  • 200
  • 2
  • 5
0

Original answer said to put it in an iframe. The better solution is to use the events offsetX and offsetY on a canvas that has the padding set to 0px.

<html>
<body>
<script>

var main=document.createElement('canvas');
main.width="200";
main.height="300";
main.style="padding:0px;margin:30px;border:thick dashed red";
document.body.appendChild(main);

// adding event listener

main.addEventListener('mousemove',function(e){
    var ctx=e.target.getContext('2d');
    var c=Math.floor(Math.random()*0xFFFFFF);
    c=c.toString(16); for(;c.length<6;) c='0'+c;
    ctx.strokeStyle='#'+c;
    ctx.beginPath();
    ctx.arc(e.offsetX,e.offsetY,3,0,2*Math.PI);
    ctx.stroke();
    e.target.title=e.offsetX+' '+e.offsetY;
    });

// it worked! move mouse over window

</script>
</body>
</html>
0

Based on @Patrick Boos solution but fixing potential problem with intermediate scrollbars.

export function getRelativeCoordinates(event: MouseEvent, referenceElement: HTMLElement) {
  const position = {
    x: event.pageX,
    y: event.pageY,
  };

  const offset = {
    left: referenceElement.offsetLeft,
    top: referenceElement.offsetTop,
  };

  let reference = referenceElement.offsetParent as HTMLElement;

  while (reference) {
    offset.left += reference.offsetLeft;
    offset.top += reference.offsetTop;
    reference = reference.offsetParent as HTMLElement;
  }

  const scrolls = {
    left: 0,
    top: 0,
  };

  reference = event.target as HTMLElement;
  while (reference) {
    scrolls.left += reference.scrollLeft;
    scrolls.top += reference.scrollTop;
    reference = reference.parentElement as HTMLElement;
  }

  return {
    x: position.x + scrolls.left - offset.left,
    y: position.y + scrolls.top - offset.top,
  };
}
Emanuele Pavanello
  • 785
  • 1
  • 9
  • 22
0

I had to get the cursor position inside a very wide div with scrollbar. The objective was to drag elements to any position of the div.

To get the mouse position on a far away position deep in the scrolling.

$('.canvas').on('mousemove', function(e){
    $(dragElement).parent().css('top', e.currentTarget.scrollTop + e.originalEvent.clientY );
    $(dragElement).parent().css('left', e.currentTarget.scrollLeft + e.originalEvent.clientX )
});
ahwelp
  • 77
  • 2
  • 8
0

As I didnt find a solution that would help you get it if you append it into a parents element where you have a e.g. selection.

This is what I did:

let positions = {
  x: event.pageX,
  y: event.pageY - event.currentTarget.getBoundingClientRect().top + event.currentTarget.offsetTop
}
Oleg Barabanov
  • 2,468
  • 2
  • 8
  • 17
semi
  • 1
0

Possible approach:

  • The mousemove event is attached to the myElement element.
  • When the mouse moves within that element, a listener function (e.g. handleMouseMove) is called.
  • Inside that listener function, myElement.getBoundingClientRect() can used to get the element's position relative to the viewport.
  • Then, the event.clientX and event.clientY properties of the event object are used to calculate the mouse position relative to the element by subtracting the element's top-left coordinates.
  • Finally, the mouse position relative to the element can be used (e.g. logged to the console).
Gerrit Bertier
  • 4,101
  • 2
  • 20
  • 36
-1

Here is what I got.

    $(".some-class").click(function(e) {

    var posx = 0;
    var posy = 0;

    posx = e.pageX;
    posy = e.pageY;

    alert(posx);
    alert(posy);
});
ChrisF
  • 134,786
  • 31
  • 255
  • 325
Shmoo
  • 1