JavaScriptでマウスカーソルがポイントしている要素(:hover)を取得する方法

さっき書いたエントリは前フリでこっからが本題。

マウスカーソルが乗っている要素を直接取得する関数があったら色々と便利です。ゲームなどにも使えそうですし。
document.elementFromPoint(x, y) とすることで x,y直下の要素を取得できますが、Firefox2 には elementFromPoint が実装されていません。
そこで、

:hover { word-spacing: 2px }

CSS を設定し、document.defaultView.getComputedStyle() + style.wordspacing === "2px" な要素を検索すれば、Firefox2 でも マウスカーソルがポイントしている要素(:hoverな要素)を取得できそうです。

具体的にはこのようにします。

<script>
  // uu.event.hover - マウス直下の要素を返す
  uu.event.hover = function(evt) {
    var rv, mpos;
    if (document.elementFromPoint) {
      mpos = uu.event.mousePos(evt);
      return document.elementFromPoint(mpos.x, mpos.y); // APIを使用する
    }
    rv = uu.css(":hover"); // CSSセレクタで :hover な要素(style.wordspacing === "2px")を検索する
    return rv.length ? rv[0] : null; // 見つからなければnullを返す
  }

  uu.ready(function() {
    function F(evt) {
      uu.event.stop(evt);
      switch (uu.event.toType(evt)) {
      case "mousemove": 
        var e = uu.event.hover(evt); // マウスのx,y座標が示す要素を取得する
        if (e) {
          // 見つかった
        }
      }
    }
    uu.event.add(F, document, "mousemove", true); // マウスキャプチャー
  });
</script>

できなかったこと

当初は、Firefox2 で document.elementFromPoint を擬似的に実装しようとしていましたが、
それには、

  • こっそりマウスをキャプチャーする
  • 1px以上ユーザがマウスを動かす( これどうする? )
  • こっそりキャプチャーを解除する

と、ユーザが介入する手順が必要だったため、elementFromPoint の実装は無理でした。

そこで頭をひとひねり。

  1. document.elementFromPoint でやりたいことは特定位置の要素を取得すること。
  2. 座標 + 要素 = マウスカーソル直下の要素が欲しいんだよねきっと。
  3. それって:hoverな気がする。
  4. :hoverを実装すれば document.elementFromPoint でやりたかったことが実装できるんでは?
  5. :hoverってどうやれば取れるの?
  6. uu.css(":hover")を実装しよう。
  7. そのためには、CSSで :hover { なにやら } とやって、「なにやら」が適用された要素を検索すれば早くね?
  8. それだ!

と、このような流れで生まれたのが、:hover な要素を取得する uu.event.hover() です。

uu.event.hover は uupaa.js (0.6) に入れる予定です。
あと、副産物として CSSセレクタ(uu.css())で、:hover と :focus が取れるようになりました(IEOperaは:hoverのみ)