• getBoundingClientRect()はviewport基準の座標を返し、ページ位置にはscroll offsetを足す

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を加えます。

戻る