Opera用Hit-a-Hint作成記・経過報告 5

Opera で getClientRects と getBoundingClientRect の挙動がものすごく変

この前書いたの続き。なんとなく分かってきたので、詳しく書く。


新しい Hit-a-Hint が動かなかったこちらのページを検証していて発見した。

HTML ソースは切り詰めるとこんな感じになっていて、

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html dir="ltr">
<head>
<meta http-equiv="Content-type" content="text/html; charset=UTF-8">
<style type="text/css">
.topic img {
  display: block;
}
</style>
</head>

<body>
<DIV class="topic" >
  <A href="http://slashdot.jp/search.pl?tid=92" >
    <IMG src="http://images.slashdot.jp/topics/topicsecurity.gif"  width="59"  height="78"  alt="セキュリティ"  title="セキュリティ" >
  </A>
  <A href="http://slashdot.jp/search.pl?tid=51"  class="topic2" >
    <IMG src="http://images.slashdot.jp/topics/topicmozilla.gif"  width="80"  height="64"  alt="Mozilla"  title="Mozilla" >
  </A>
  <A href="http://slashdot.jp/search.pl?tid=41"  class="topic2" >
    <IMG src="http://images.slashdot.jp/topics/topicie.gif"  width="64"  height="64"  alt="インターネットエクスプローラ"  title="インターネットエクスプローラ" >
  </A>
</DIV>
</body>
</html>

この .topic img { display: block; } があるときに、下にのような変なことが起こる。

各要素に対して getClientRects と getBoundingClientRectを取る。括弧内の数字は、[left,top,right,bottom] の値。

テスト 1

(1) 最初のブロック

getBoundingClientRect() getClientRects()[0]
a [0,0,0,0] (おかしい) 失敗
img [8,8,67,86] (正常) [8,8,67,86] (正常)

(2) 2番目のブロック

getBoundingClientRect() getClientRects()[0]
a [0,0,0,0] (おかしい) 失敗
img [8,86,88,150] (正常) [8,86,88,150] (正常)

(3) 最後のブロック

getBoundingClientRect() getClientRects()[0]
a [8,134,8,155] (幅がゼロ?) [8,134,8,155] (幅がゼロ?)
img [8,150,72,214] (正常) [8,150,72,214] (正常)

「失敗」のときはエラーコンソールにも何も出ない。

Firefox では全部正常に取得できた。但し、HTML を詰めて書いていないため、ホワイトスペース (つまりテキストノード) の Client Rect も取得するらしく、getClientRect()[0] は幅がゼロの長方形を返し、getClientRect()[1] でやっと子要素の長方形を返すということがあったりする。

Opera でももしかしたらと思い、 (3) の a 要素で getClientRects().length としてみたら、ちゃんと(?)「1」でした。

テスト 2

次に、.topic img { display: block; } を消してみる (つまり inline 表示)。この場合、最初のブロックとその他にレンダリングの性質上の違いはない。

getBoundingClientRect() getClientRects()[0]
a [8,8,67,91] (画像+テキスト領域) [8,70,67,91] (テキスト領域)
img [8,8,67,86] (画像領域) [8,8,67,86] (画像領域)

つまりこういうこと。(黄色が画像+テキスト領域、オレンジが画像領域、黄緑がテキスト領域)

こちらも案の上 a 要素のほうで getClientRects().length==1 だった。

Firefox だと、すべてにおいてテキスト領域を返した。インライン要素だからそういうことなのかもしれないけど、実際の表示では中の大きいの要素に合わせているのだから、Bounding (要素を「縛って」いる) Client Rect もそれに合わせて広がる気べきな気がする。

テスト 3

.topic img { display: block; } を元に戻して、最初のブロックの img 要素を消す。つまり a 要素の innerHTML が無い状態にする。

すると、 その a 要素で、このようになる。

getBoundingClientRect() getClientRects()[0]
a [0,0,0,0] (おかしい) 失敗

テスト 4

テスト 3 の状態で、再度 .topic img { display: block; } を消して、最初の a 要素の後に < br > タグを入れる。

結果はテスト 3 と同じ。

getBoundingClientRect() getClientRects()[0]
a [0,0,0,0] (おかしい) 失敗

テスト 4 は、実はこの前書いた場合と同じである。

結論

Opera では、要素の内部が何か知らないうちは getBoundingClientRect も getClientRects も使うな。


さて、Hit-a-Hint を修正しないと。


書いてみた。かなりその場凌ぎ的ではあるけど。

もうほぼ完成に近いと思うんだけどなあ。


まだあった。下のようなときに getClientRects が失敗する。

<A href="http://type.jp/s/navi/0801/index2.html?000207TU012733=PID" >
  <BR>
</A>

こんな HTML 書くなよー!