offsetTop / offsetLeft とは何か
offsetTop と offsetLeft は、「その要素が“基準の親”(offsetParent)からどれだけ離れているか」を示すピクセル値です。縦方向が offsetTop、横方向が offsetLeft。ここが重要です:この“基準の親”は単なる親ではなく、レイアウト上の基準点になる要素(通常は最近の position が指定された祖先、なければ body/html)です。つまり offsetTop/Left は“ページ全体の座標”ではなく、“offsetParent からの相対座標”です。
offsetParent の仕組み(誰が基準になるのか)
どう決まるか
- 最近の祖先に position: relative / absolute / fixed のいずれかが付いていれば、その祖先が offsetParent になります。
- そうした祖先がなければ、通常は body(ブラウザによっては html)が offsetParent になります。
- display: none のときや、position: fixed の要素などでは挙動が特殊になり、offsetParent が null になるケースもあります。
ここが重要です:offsetTop/Left を使う前に、element.offsetParent が何かを確認しましょう。同じ要素でも、CSS の position を付けるかどうかで値の意味が大きく変わります。
基本の使い方(相対座標の取得とページ座標への変換)
相対座標を読む
<div id="parent" style="position:relative; margin:40px; padding:20px; border:4px solid #ccc">
<button id="child" style="position:absolute; left:100px; top:60px">ボタン</button>
</div>
<script>
console.log(child.offsetParent === parent); // true
console.log(child.offsetLeft, child.offsetTop); // 100, 60(parentからの距離)
</script>
HTMLここが重要です:offsetLeft/Top は“parent からの距離”を返します。margin やページ上の位置は無視され、あくまで offsetParent 基準のピクセルです。
ページ全体の座標(ドキュメント座標)に変換する
<script>
function getPageOffset(el) {
let x = 0, y = 0;
let node = el;
while (node) {
x += node.offsetLeft;
y += node.offsetTop;
node = node.offsetParent;
}
return { x, y };
}
console.log(getPageOffset(child)); // ページ左上からの座標
</script>
HTMLここが重要です:offsetParent を辿って offsetLeft/Top を足し上げると、“ページ左上からの絶対座標”が得られます。スクロール位置は考慮されないので、ビューポート位置が欲しいときは次の方法を使います。
getBoundingClientRect との違い(どちらを使うかの判断)
ビューポート座標が欲しいなら getBoundingClientRect
<script>
const r = child.getBoundingClientRect();
console.log(r.left, r.top); // 画面(ビューポート)左上からの位置
</script>
HTMLここが重要です:getBoundingClientRect は“画面に対しての位置とサイズ”を返します。スクロールの影響も反映されます。対して offsetTop/Left は“offsetParentに対しての位置”で、スクロールを直接は反映しません。
どちらを選ぶか
- 要素の“配置計算”(親との相対位置)やドラッグの基準が必要なら offsetTop/Left。
- スクロールや画面端との交差判定、ポップアップの表示位置合わせなら getBoundingClientRect。
ここが重要です:目的に合わせて使い分けると、計算がシンプルで誤差が出づらくなります。
スクロールと border/padding の影響(値の意味を正しく読む)
スクロールについて
- offsetTop/Left 自体はスクロール値を含みません。“相対位置”だけを表します。
- ページのスクロール量は window.scrollY/scrollX、要素のスクロール量は element.scrollTop/scrollLeft で別途取得します。
- “画面に対しての位置”が欲しい場合は getBoundingClientRect を使う方が早いです。
border/padding の扱い
- offsetLeft/Top は“border の外側”からの距離を返します。offsetParent の border があると、その分だけ基準がシフトします。
- padding は子の配置に影響しますが、offsetLeft/Top は結果的にその配置後の値を返すため、親の padding 分の見かけ位置が変わります。
ここが重要です:border/padding のある親を基準にすると「思ったより値がずれる」ことがあります。CSS と値の定義の関係を意識しましょう。
実践例(スクロール合わせ、クリック位置へのメニュー表示、アニメーション)
要素までスクロールする(ヘッダ分の余白を考慮)
<header style="position:sticky;top:0;height:64px;background:#fff"></header>
<section id="target" style="margin-top:800px">ここへ移動</section>
<script>
function scrollToElement(el, offset = 0) {
const { x, y } = getPageOffset(el); // 先ほど定義した関数
window.scrollTo({ left: x, top: y - offset, behavior: "smooth" });
}
// スティッキーなヘッダの高さぶんを引いてスクロール
scrollToElement(target, 64);
</script>
HTMLここが重要です:ページ座標にヘッダ高などの“固定要素の重なり”を差し引くと、ちょうど見える位置に止まります。
クリックした要素の右下にメニューを出す(親基準)
<div id="parent" style="position:relative">
<button id="btn" style="position:absolute; left:100px; top:60px">ボタン</button>
<div id="menu" style="position:absolute; display:none">メニュー</div>
</div>
<script>
btn.addEventListener("click", () => {
// 親を基準にして、子の右下へ配置(相対座標がそのまま使える)
menu.style.left = (btn.offsetLeft + btn.offsetWidth) + "px";
menu.style.top = (btn.offsetTop + btn.offsetHeight) + "px";
menu.style.display = "block";
});
</script>
HTMLここが重要です:同じ offsetParent の中で位置合わせをするなら、offsetTop/Left と offsetWidth/Height の組み合わせが最も簡単です。
要素を親内で滑らかに移動(相対座標を直接更新)
<script>
let x = btn.offsetLeft, y = btn.offsetTop;
function move(dx, dy) {
x += dx; y += dy;
btn.style.left = x + "px";
btn.style.top = y + "px";
}
// 例:右へ10px、下へ6px
move(10, 6);
</script>
HTMLここが重要です:親内のアニメーションなら、“相対座標を更新して style に反映”がシンプルでズレにくいです。
よくある落とし穴と回避策
offsetParent が意図せず body になる
親に position を付け忘れると、offsetParent が body になり、相対座標が大きな値になります。回避策:位置合わせの“グループ”には position: relative を明示して、相対座標の基準を作る。
スクロールと混同して座標が合わない
offsetTop/Left はスクロールを含みません。画面上の見え方と一致させたいなら getBoundingClientRect を使うか、window.scrollY/scrollX を加減算して調整する。
border/padding で基準がズレる
親の border/padding があると基準点が変わります。回避策:レイアウトの意図に合わせて、基準にしたい親の border/padding を把握するか、基準専用のラッパーを用意する。
position: fixed の要素で offsetParent が null
固定配置は通常のフロー外です。回避策:画面座標は getBoundingClientRect を使う。親相対の配置が必要なら fixed は使わず relative/absolute に設計を変える。
まとめ
offsetTop/offsetLeft は「offsetParent(レイアウトの基準となる親)からの相対位置」を返すプロパティです。相対座標が欲しい場面(同じ親内での位置合わせ・アニメーション)に向き、ページ座標や画面座標が欲しいなら、足し上げて絶対座標を作るか、getBoundingClientRect を使います。スクロールは別途 window.scrollY/element.scrollTop で扱い、border/padding が基準点に影響することを理解する。offsetParent を意識して設計すれば、初心者でも正確でズレの少ない位置計算と UI 配置が実装できます。

