2009-04-14

JavaScriptで要素上のマウスカーソル座標を求める

マウスイベントオブジェクトから得られる座標をある要素上の座標系へ変換したいのだが、これがなかなか簡単にはいかない。

色々調べてみるとgetBoundingClientRectというものがIEやFirefox、Operaでは使えるようなので、それを使うことにした。WebKitはつい最近実装したばかりのようだ。なので、getBoundingClientRectが使えればそれとclientX,clientYとの差分で求める。使えなければ、要素の絶対座標を適当に推測して、pageX,pageYとの差分で求める(IEはpageX,pageYをサポートしていないがgetBoundingClientRectの方を使うので問題ない)。

function getElementAbsPos(elem)
{
  var x = 0;
  var y = 0;
  while(elem){
    x += elem.offsetLeft;
    y += elem.offsetTop;
    elem = elem.offsetParent;
  }
  return {x:x, y:y};
}

function getMousePosOnElement(elem, ev)
{
  if(!ev){ ev = event;}
  if(elem.getBoundingClientRect){
    var cr = elem.getBoundingClientRect();
    var x = ev.clientX - cr.left;
    var y = ev.clientY - cr.top;
    return {x:x, y:y};
  }
  else if(typeof(ev.pageX) == "number" && typeof(ev.pageY) == "number"){
    var pos = getElementAbsPos(elem);
    return {x:ev.pageX-pos.x, y:ev.pageY-pos.y};
  }
  else{
    return {x:0, y:0};
  }
}

function onClick(elem, ev)
{
  var pos = getMousePosOnElement(elem, ev);
  alert(pos.x + "," + pos.y);
}

var elem = document.getElementById( ???? );
elem.onclick = function(ev) { onClick(elem, ev);}

とりあえず簡単なスクロール込みのhtmlでテストした感じでは大丈夫っぽい。CSSの指定によってはgetElementAbsPosあたりが怪しいけど。

マウスの要素内座標が簡単に求められないなんて、本当にどうかしていると思う。