event.offsetX と offsetY の互換性について

IE9正式版ではなく IE9pp(IE9 Platform Preview) の現状について記載しています。

event.offsetX/Y や event.layerX/Y から相対座標を取得できますが、DOM Lv0 のため仕様が無く、互換性もありません。今日はそれらの非互換性について調べてみました。

event.offsetX/Y は、IE, Opera, WebKit で定義されており、event.layerX/Y は、GeckoWebKit で定義されています(WebKitのlayerX/YはGeckoと別物です)。

event.offsetX/Y は、現在標準化にむけて策定中です http://www.w3.org/TR/cssom-view/#extensions-to-the-mouseevent-interface
策定中の仕様では event.offsetX/Y は padding edge を基準にするため、IE6〜IE8 の仕様に合わせることになりそうです。
IE の動作に合わせると、遅いブラウザ(IE)では何もしなくてもよくなり、速いブラウザで計算量が増えることになりますが、(今回は)ライブラリを実装する側としてはむしろ都合が良かったりします。

デモ

mousemove イベントを使い、各ブラウザの返す値を調べてみます
http://pigs.sourceforge.jp/blog/20100430/test/demo.event/uu.event.htm


# 左から、IE8, Opera10.52, Safari4, Firefox3.6.3

赤いカーソルの先端が、offsetX = 0, offsetY = 0 になります。

<style>
.relative {
    position: relative; left: 100px; top: 200px;
    padding: 10px;
    border: 10px solid navy;
    background-color: blue;
}
</style>
[CSS2.1 box model] http://www.w3.org/TR/CSS2/box.html

    B-------border--------+ -> border edge [CSS2.1 keyword]
    |                     |
    |  P----padding----+  | -> padding edge [CSS2.1 keyword]
    |  |               |  |
    |  |  C-content-+  |  | -> content edge [CSS2.1 keyword]
    |  |  |         |  |  |
    |  |  |         |  |  |
    |  |  +---------+  |  |
    |  +---------------+  |
    |                     |
    +---------------------+

    B = event.offsetX/Y in WebKit
        event.layerX/Y  in Gecko
    P = event.offsetX/Y in IE6 ~ IE8
    C = event.offsetX/Y in Opera

デモを動かすと、

  • Gecko の event.layerX/Y は、B の位置をゼロとした値。layerX/Y は正の値だけ。
  • WebKit の event.offsetX/Y は、B の位置をゼロとした値。offsetX/Y は正の値だけ。
  • IE の event.offsetX/Y は、P の位置をゼロとした値。offsetX/Y には負の値がありうる。
  • Opera の event.offsetX/Y は、C の位置をゼロとした値。offsetX/Y には負の値がありうる。

といったことが分かります。

絶対座標の取得

スクロール座標を加味したページ左上からの絶対座標は、event.pageX/Y または event.clientX/Y + スクロール成分で取得できます。
IE6 〜 IE8 には event.pageX/Y が無いため、以下のようにします。

  event.pageX = event.clientX + document.documentElement.scrollLeft;
  event.pageY = event.clientY + document.documentElement.scrollTop;

相対座標の取得

イベントターゲットの要素を基準とした相対座標(最寄の要素から見た相対座標)は、event.offsetX/Y から取ります。
ボーダーやパディングが設定されていなければ取れてくる値には互換性があります。

ボーダーやパディングが設定されている場合は…

  • operaなら、getComputedStyle で border-top-width, border-left-width と padding-top, padding-left を取得し、それらを event.offsetX/Y の値に足す
  • IE なら currentStyle から border-top-width, border-left-width の値を取得し、単位を pixel に変換し、event.offsetX/Y の値に足す

といった面倒な手順が必要になります。

IEで、border-width に px 以外の単位を指定すると、リフローを伴う単位変換処理が必要になり遅くなります。
イベントハンドラ内部で毎回変換するのはやめたほうが良いでしょう。

IE9pp には offsetX/Y が無い

IE9pp には event.pageX/Y, event.offsetX/Y も存在せず、event.clientX/Y と event.screenX/Y だけが実装されています。
今のところ簡単に相対座標を取得する方法は提供されていないようです。

耳寄り

window.eventの方ならevent.x とevent.offsetXは取れますね。単に実装漏れかと。

via os0x http://twitter.com/os0x/status/13073522323

IE9pp の window.event はIE8以下との互換用で、 handleEvent(event) の event は DOM Level3 Event にほぼ従ってるっぽい。 window.event には relatedTarget などがないの
via http://twitter.com/uupaa/status/13115227590

window.event には x, y, offsetX, offsetY が残ってます。また、window.event.offsetX/Y の原点は(P = padding edge) でIE6〜IE8と変わりないため、不足している情報は(とりあえず今は)そちらを参照すればよさそうです
id:os0xさん ナイスな情報ありがとうございます!