Getting the Exact Mouse Position
Following up on a thread we left unexplored a few moments ago, the clientX
and clientY
properties don’t give you the exact mouse position in many situations. Understanding the details of why is something that I cover in this article, but the gist of it is that the clientX
and clientY
properties don’t account for your canvas
element physically being pushed around by all of its ancestors. A margin or padding here and there can cause your positioning inside the canvas
element to go out-of-sync. It’s really quite sad.
The solution is to pair up your clientX
and clientY
properties with the following code (taken from the Get an Element’s Position Using JavaScript article) that takes into account all of the various position-related shenanigans that your canvas
might be affected by:
// Helper function to get an element's exact positionfunction getPosition(el) {var xPos = 0;var yPos = 0;while (el) {if (el.tagName == "BODY") {// deal with browser quirks with body/window/document and page scrollvar xScroll = el.scrollLeft || document.documentElement.scrollLeft;var yScroll = el.scrollTop || document.documentElement.scrollTop;xPos += (el.offsetLeft - xScroll + el.clientLeft);yPos += (el.offsetTop - yScroll + el.clientTop);} else {// for all other non-BODY elementsxPos += (el.offsetLeft - el.scrollLeft + el.clientLeft);yPos += (el.offsetTop - el.scrollTop + el.clientTop);}el = el.offsetParent;}return {x: xPos,y: yPos};}
Here is an example of this code in action inside our canvas
element:
var canvas = document.querySelector("#myCanvas");var context = canvas.getContext("2d");var canvasPosition = getPosition(canvas);canvas.addEventListener("mousemove", doSomething, false);// take into account page scrolls and resizeswindow.addEventListener("scroll", updatePosition, false);window.addEventListener("resize", updatePosition, false);function updatePosition() {canvasPosition = getPosition(canvas);}function doSomething(e) {// get the exact mouse X and Y coordinatesvar mouseX = e.clientX - canvasPosition.x;var mouseY = e.clientY - canvasPosition.y;// print it to the consoleconsole.log("The mouse position is: " + mouseX + ", " + mouseY);}
The canvasPosition
variable stores the object returned by the getPosition
function, and the mouseX
and mouseY
variables store the exact position once you combine canvasPosition
's result with the clientX
and clientY
value:
var mouseX = e.clientX - canvasPosition.x;var mouseY = e.clientY - canvasPosition.y;
For completeness, we even listen to the window
resize and scroll events to update the value stored by canvasPosition
:
// take into account page scrolls and resizeswindow.addEventListener("scroll", updatePosition, false);window.addEventListener("resize", updatePosition, false);function updatePosition() {canvasPosition = getPosition(canvas);}
We do this to ensure that our mouse position values remain accurate even if users scroll the page or resize the browser window. All of the code you see here might be going away in the future, though. There is a scheme underway to add an offsetX
and offsetY
property to the MouseEvent
object that automatically takes care of all this.