Operaはライブラリ側の創意工夫でなんとか出来ないことが多い

他のブラウザで出来るのにOperaで出来ないこと

ユーザーに気づかれないようにスタイルを設定しづらい

ブラウザをHackして、本来実装されていない機能を実現するために、CSSを使って要素をマーキングをしたい場合があります。
そのような場合は、ユーザには気づかれないような(描画に影響せず滅多に使用されないスタイル)プロパティを使う必要があります。

<style>
#id1 { vertical-align: 0.1px } // デフォルト値はbaseline, 0を指定するとbaselineと同じ描画結果になる
#id2 { outline: 1px solid transparent } // デフォルト値は medium none invert (2px none black に近い) 
</style>

で、
#id1 のgetComputedStyle() を見ると 0 が返ってくる。(IEは試してないけど)他のブラウザではちゃんと0.1pxが返ってくる。0.1を返してくれないと困るのに丸められる。
#id2 は透過されずに黒い枠線が表示される。透過させる気が無いらしい(IEはoutline未実装)。
CSS3のColorモジュールの仕様では、color値を受け取れる場所では transparent が使えるらしいのだが。
# http://www.w3.org/TR/2003/CR-css3-color-20030514/#transparent

vertical-alignとoutlineを例に出したけど、こういうのが結構ある。

他のブラウザのように描画に影響しないCSSプロパティ自体がOperaは少ないのと + getComputedStyle()で取れる値が改変されており使えないため、このような裏技がOperaでは使いづらい。

こんな裏技を駆使してまでクロスブラウザを達成しようとしているのは、世界で私一人だけかもしれないから、Operaに文句は言えないが。

挙動が異なる

document.elementFromPoint(x, y) が IEや他のブラウザと違う挙動になる
x,yでポイントしているはずなのに、文字列の上にカーソルを移動すると、undefinedが返る。
spanなどでちゃんとマークアップしてもだめ。

IEで出来るのに、Operaで出来ないことのほうが実は多い。

様々なテストをやってるけど、現時点ではOperaがもっとも自由度の低いブラウザかもね。
IEは裏技が沢山開発されており、裏技を組み合わせることで何とかなったりするが、Operaはそうじゃない。

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のみ)