Currently I am working on a project that uses the HTML5 canvas element. But this post is not really about canvas elements so you can read on without fear, even if you are unfamiliar with them.

For this project I will have to be able to click on the canvas element and find the offset of the cursor from the top/left of the canvas element. This proved harder than I initially thought it would be.

The click event object contains two parameters: event.pageX and event.pageY. They give the offset of the cursor from the top/left of the page [actually from the top/left of the HTML element]. So if I knew the coordinates of the top/left of the canvas object then the following function would be what I needed:

javascript Code:
function offsetIn(event, obj) {
  var a, b;
  [a, b] = findPos(obj);
  var x = event.pageX - a;
  var y = event.pageY - b;
  return [x, y];
}

The findPos() function would have to return the coordinates of the top/left of the canvas object. And this is where it gets complicated.

At first I thought that I could use a function like this for findPos():

javascript Code:
function findPos(obj) {
  var offset = {x : 0, y : 0};
  getOffset (obj, offset);
  var posX = offset.x;
  var posY = offset.y;
  return [posX, posY]
}

This findPos() function uses the following recursive getOffset function to find the offset of each object within its containing object while recursing up the object tree. And this will work as long as no scrolling occurs in any of these objects.

javascript Code:
function getOffset (object, offset) {
  if (!object) {return;}
  offset.x += object.offsetLeft;
  offset.y += object.offsetTop;
  getOffset (object.offsetParent, offset);
}

But what if scrolling does occur? Then the returned values will be incorrect. To fix this we have to find all the scroll offset values and adjust posX and posY accordingly. The following recursive function calculates these adjustment values.

javascript Code:
function getScrolled (object, scrolled) {
  if (!object) {return;}
  scrolled.x += object.scrollLeft;
  scrolled.y += object.scrollTop;
  if (object.tagName.toLowerCase() != "html") {
    getScrolled (object.parentNode, scrolled);
  }
}

So the final findPos() function ends up looking like this:

javascript Code:
function findPos(obj) {
  var offset = {x : 0, y : 0};
  getOffset (obj, offset);
  var scrolled = {x : 0, y : 0};
  getScrolled (obj.parentNode, scrolled);
  var posX = offset.x - scrolled.x;
  var posY = offset.y - scrolled.y;
  return [posX, posY]
}

If anyone out there knows of a simpler way to do this, please let me know.