JavaScript | DOM 操作:要素の位置・サイズ・スクロール – offsetTop / offsetLeft

JavaScript JavaScript
スポンサーリンク

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 配置が実装できます。

タイトルとURLをコピーしました