getBoundingClientRect()はviewport基準の座標を返し、ページ位置にはscroll offsetを足す
公開日: 執筆:佐藤
getBoundingClientRect()のleftとtopは、表示中のviewportを基準にした座標です。ページの左上を基準にした位置が必要なら、横方向へwindow.pageXOffset、縦方向へwindow.pageYOffsetを足します。
スクロール後にrect.topの値が変わるのは、要素が文書内を移動したからとは限りません。viewportの上端と要素との距離が変わったためです。まず、欲しい位置がviewport基準かページ基準かを決めます。
getBoundingClientRect()は要素を囲む矩形を返す
W3CのCSSOM View Moduleは、getBoundingClientRect()を、要素のclient rectangleを囲む最小の矩形を返すメソッドとして定義しています。複数行に折り返されたinline要素など、要素が複数の矩形を持つ場合も、その全体を囲む矩形を一つ受け取れます。
位置を調べるときによく使う値は次の四つです。
| プロパティ | 示す位置 |
|---|---|
left |
viewport左端から矩形左端まで |
top |
viewport上端から矩形上端まで |
right |
viewport左端から矩形右端まで |
bottom |
viewport上端から矩形下端まで |
これらはviewport基準です。要素が画面より上へ外れれば、topが負の値になることもあります。ページ内の絶対的な座標と決めつけず、画面内の位置を測る値として使います。
ページ位置にはpageXOffsetとpageYOffsetを足す
ページ全体の左上を基準にした位置は、viewport基準の座標へ現在のscroll offsetを加えて求めます。
var target = document.getElementById('target');
var rect = target.getBoundingClientRect();
var viewportPosition = {
left: rect.left,
top: rect.top
};
var pagePosition = {
left: rect.left + window.pageXOffset,
top: rect.top + window.pageYOffset
};
これは計算方法を示すための例です。viewportPositionは表示領域内の位置、pagePositionはページ内の位置を表します。
横方向のスクロール量にはpageXOffset、縦方向にはpageYOffsetを使います。名前と軸を逆にしないようにします。縦長のページだけを試すとpageXOffsetが常に0になり、間違いに気づきにくいため、横方向へスクロールできる状態でも一度確認します。
目的に応じて座標の基準を選ぶ
座標は、使い道によって必要な基準が変わります。
| 目的 | 使う値 |
|---|---|
| 要素が表示領域へ入ったか調べる | rect.top、rect.bottomなどのviewport基準値 |
| viewport内へ案内を重ねる | rect.left、rect.topを配置方法に合わせて使う |
| ページ全体に対する要素位置を記録する | rect.left + pageXOffset、rect.top + pageYOffset |
| 祖先要素を基準にしたCSS配置を調べる | offsetLeft、offsetTopや配置規則を別に確認する |
たとえば、「要素が画面上端を通過したか」という判定にページ位置を使うと、現在のviewportとの関係をもう一度計算することになります。反対に、ページ内の目印までの距離を保存したい場合、rect.topだけではスクロール位置によって値が変わります。
必要な基準を先に決めれば、同じ数字を別の意味で使う間違いを減らせます。
offsetTopとoffsetLeftは同じ座標ではない
offsetTopとoffsetLeftは、getBoundingClientRect()の別名ではありません。これらはoffsetParentとの関係から位置を求める値です。一方、getBoundingClientRect()はviewportを基準にします。
CSS transformが使われている場合も区別が必要です。CSSOM View Moduleでは、client rectangleを求める際にtransformを適用しますが、offsetTopとoffsetLeftの計算ではtransformを無視します。二つの方法が同じ値になることを前提にしません。
絶対配置された部品の基準を調べたい場合は、座標値だけでなく、positionを指定した祖先も確認します。画面に見えている位置と、CSSが配置を計算する基準は別の問題です。
取得した矩形は必要な時点で測り直す
getBoundingClientRect()が返す矩形は、取得した時点の静的な値です。取得後にスクロールしたり、画像の読込みや文字量の変更でlayoutが変わったりしても、保存済みのrectが自動で更新されるわけではありません。
スクロール後の画面位置が必要なら、位置を使う直前にgetBoundingClientRect()を再び呼びます。ページ位置が必要な場合は、同じ時点のpageXOffsetとpageYOffsetを加えます。
要素の追加、画像の読込み、文字サイズの変更などでlayoutが変わった場合も、変更後に測り直します。返り値は小数になる可能性があるため、用途が決まる前に整数へ丸めません。
スクロール処理のたびに多数の要素を測るときは、必要な要素と取得回数も絞ります。位置を正しく求めることと、処理回数を管理することを分けて考えてください。
CSSOM View ModuleはWorking Draftです。実装へ使う前に、対象ブラウザーで返り値、スクロール、transform、小数の扱いを確認します。仕様で定義された計算と、運用対象のブラウザーでの動作確認は別の作業です。
最初の一手は、既存コードでrect.topを使っている箇所を一つ選び、その値がviewport基準とページ基準のどちらで必要かを書き出すことです。ページ基準なら、取得した時点のpageYOffsetを加えます。
関連記事

