【JavaScript】要素の位置とサイズを正確に取得!getBoundingClientRect()での実装アイデア3選

JavaScriptでUIを実装する際、「対象の要素が画面のどの位置にあるか」「幅や高さはどれくらいか」を正確に知りたい場面が多々あります。 そんな時に欠かせないのが getBoundingClientRect() メソッドです!

このメソッドを実行すると、対象要素の top, left, right, bottom, width, height など、ビューポート(ブラウザの表示領域)を基準とした座標やサイズ情報のオブジェクトを取得できます。

今回は、このメソッド単体の説明だけでなく、「実際にどう使われるのか?」がわかるように、定番のUI機能を3つ実装してご紹介します!

デモ動作

See the Pen getBoundingClientRect()の実用例まとめ by mikanbako (@mikanbako) on CodePen.

※動作確認に関する補足
CodePenの埋め込みプレビュー上では、iframeの仕様により記事全体のスクロールを正しく検知できない場合があります。
全画面でのデモ動作は以下のリンクからご確認下さい!(別タブで開かれます)

実装コード紹介

【HTML】

<div class="l-wrap">
  <h2>getBoundingClientRect() 活用デモ</h2>

  <!-- 活用例1:ツールチップ -->
  <section class="p-section">
    <h3>1. ツールチップの表示位置計算</h3>
    <p>ボタンの座標とサイズを取得し、その真上にツールチップを表示します。</p>
    <button class="c-btn js-tooltip-btn">Hover Me!</button>
    <!-- ツールチップ本体(JSで位置を制御) -->
    <div class="c-tooltip js-tooltip">ここだよ!</div>
  </section>

  <!-- 活用例2:マグネットボタン -->
  <section class="p-section">
    <h3>2. 座標を計算して吸い付く「マグネットボタン」</h3>
    <p>要素の中心座標を計算し、マウスの動きに追従させます。</p>
    <button class="c-btn c-btn--magnet js-magnet-btn">吸い付くよ!</button>
  </section>

  <!-- 活用例3:スクロール判定 -->
  <section class="p-section">
    <h3>3. スクロール領域の判定</h3>
    <p>下の箱が画面内に入ったかを判定します。スクロールしてみてね!</p>
    <div class="p-scroll-space">↓ スクロールしてね ↓</div>
    <div class="c-fade-box js-fade-box">画面に入ると色が変わるよ!</div>
  </section>
</div>

【CSS】

/* ベース装飾 */
.l-wrap { 
  max-width: 800px;
  margin: 0 auto;
  padding: 40px 20px;
  color: #3e3e3e;
  text-align: center;
}
h2{
  font-size: 16px;
  color: #1bb4d4;
  margin-bottom: 24px;
}
h3{
  font-size: 16px;
  font-weight: bold;
  margin-bottom: 16px;
}
p{
  font-size: 16px;
  margin-bottom: 16px;
}
.p-section {
  margin-bottom: 40px;
  padding: 40px 20px;
  background-color: #fff;
  border-radius: 12px;
  box-shadow: 0 4px 15px rgba(0,0,0,0.05);
}

/* ボタン共通 */
.c-btn {
  padding: 16px 40px;
  font-size: 16px;
  font-weight: bold;
  color: #fff;
  background-color: #1bb4d4;
  border-radius: 30px;
  cursor: pointer;
}

/* 1. ツールチップ */
.c-tooltip {
  position: fixed; /* 画面を基準に配置 */
  background-color: #333;
  color: #fff;
  padding: 8px 16px;
  border-radius: 6px;
  font-size: 14px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease-out;
  z-index: 100;
}
.c-tooltip.is-active { 
  opacity: 1;
}

/* 2. マグネットボタン */
.c-btn--magnet {
  background-color: #e83e8c;
  transition: transform 0.1s ease-out;
}

/* 3. スクロール判定 */
.p-scroll-space { 
  display: flex;
  justify-content: center;
  align-items: center;
  height: 300px;
  font-size: 14px;
  color: #999;
}
.c-fade-box {
  width: 100%;
  padding: 60px 0;
  background-color: #ccc;
  color: #fff;
  font-size: 16px;
  font-weight: bold;
  border-radius: 12px;
  transition: background-color 0.4s ease-out;
}
.c-fade-box.is-visible {
  background-color: #28a745; /* 画面に入ったら緑に! */
}

【JavaScript】

/* =========================================
   1. ツールチップの位置計算
========================================= */
const tooltipBtn = document.querySelector('.js-tooltip-btn');
const tooltip = document.querySelector('.js-tooltip');

tooltipBtn.addEventListener('mouseenter', () => {
  // 💡 ボタンの座標とサイズを取得
  const rect = tooltipBtn.getBoundingClientRect();
  
  // ツールチップをボタンの真上の中央に配置するための計算
  // left: ボタンの左端 + (ボタンの幅の半分) - (ツールチップの幅の半分)
  // top: ボタンの上端 - ツールチップの高さ - 余白(10px)
  tooltip.style.left = `${rect.left + (rect.width / 2) - (tooltip.offsetWidth / 2)}px`;
  tooltip.style.top = `${rect.top - tooltip.offsetHeight - 10}px`;
  
  tooltip.classList.add('is-active');
});

tooltipBtn.addEventListener('mouseleave', () => {
  tooltip.classList.remove('is-active');
});

/* =========================================
   2. マグネットボタン(中心座標の計算)
========================================= */
const magnetBtn = document.querySelector('.js-magnet-btn');

magnetBtn.addEventListener('mousemove', (e) => {
  // 💡 ボタンの座標とサイズを取得
  const rect = magnetBtn.getBoundingClientRect();
  
  // 要素の中心座標(x, y)を計算
  const centerX = rect.left + (rect.width / 2);
  const centerY = rect.top + (rect.height / 2);
  
  // マウスカーソルと要素の中心との距離(差分)を計算
  const distanceX = e.clientX - centerX;
  const distanceY = e.clientY - centerY;
  
  // マウスの方向に少しだけ(係数0.3)要素を動かす
  magnetBtn.style.transform = `translate(${distanceX * 0.3}px, ${distanceY * 0.3}px)`;
});

magnetBtn.addEventListener('mouseleave', () => {
  // マウスが離れたら元の位置に戻す
  magnetBtn.style.transform = 'translate(0, 0)';
});

/* =========================================
   3. スクロール領域の判定
========================================= */
const fadeBox = document.querySelector('.js-fade-box');

window.addEventListener('scroll', () => {
  // 💡 要素の座標を取得
  const rect = fadeBox.getBoundingClientRect();
  
  // 画面の高さ(ビューポート)を取得
  const windowHeight = window.innerHeight;
  
  // 要素の上の端(rect.top)が、画面の高さの中に入ってきたら判定
  // (今回は要素が画面の下から100px見えたら発火)
  if (rect.top < windowHeight - 100) {
    fadeBox.classList.add('is-visible');
  } else {
    fadeBox.classList.remove('is-visible'); // スクロールで戻ったら外す
  }
});

コードの解説

今回の3つのデモは、すべて getBoundingClientRect() で取得した値をベースに簡単な算数をしているだけです。それぞれの重要ポイントをサクッと解説します!

ツールチップ(中心を合わせる計算)

// 💡 ボタンの座標とサイズを取得
const rect = tooltipBtn.getBoundingClientRect();
  
// ツールチップをボタンの真上の中央に配置するための計算
// left: ボタンの左端 + (ボタンの幅の半分) - (ツールチップの幅の半分)
// top: ボタンの上端 - ツールチップの高さ - 余白(10px)
tooltip.style.left = `${rect.left + (rect.width / 2) - (tooltip.offsetWidth / 2)}px`;
tooltip.style.top = `${rect.top - tooltip.offsetHeight - 10}px`;

rect.left + (rect.width / 2) という計算式で、「ボタンの水平方向の中心座標」を求めています。そこからさらに「ツールチップ自身の幅の半分(offsetWidth / 2)」を引き算することで、ボタンとツールチップの中心位置がピタッと綺麗に重なるように調整しています。

マグネットボタン(マウスとの距離を測る)

// 💡 ボタンの座標とサイズを取得
const rect = magnetBtn.getBoundingClientRect();
  
// 要素の中心座標(x, y)を計算
const centerX = rect.left + (rect.width / 2);
const centerY = rect.top + (rect.height / 2);
  
// マウスカーソルと要素の中心との距離(差分)を計算
const distanceX = e.clientX - centerX;
const distanceY = e.clientY - centerY;
  
// マウスの方向に少しだけ(係数0.3)要素を動かす
magnetBtn.style.transform = `translate(${distanceX * 0.3}px, ${distanceY * 0.3}px)`;

e.clientX, e.clientY(現在のマウスのX, Y座標)からボタンの中心座標を引くことで、「マウスと要素がどれくらい離れているか(距離)」を計算しています。その距離に対して 0.3 という係数を掛けて移動させることで、マウスの動きに少し遅れてついてくるような、気持ちいい「吸い付く動き」を表現しています!

スクロール判定(画面の高さと比較)

// 💡 要素の座標を取得
const rect = fadeBox.getBoundingClientRect();
  
// 画面の高さ(ビューポート)を取得
const windowHeight = window.innerHeight;
  
// 要素の上の端(rect.top)が、画面の高さの中に入ってきたら判定
// (今回は要素が画面の下から100px見えたら発火)
if (rect.top < windowHeight - 100) {
  fadeBox.classList.add('is-visible');
} else {
  fadeBox.classList.remove('is-visible'); // スクロールで戻ったら外す
}

rect.top は「画面の一番上から対象要素までの距離」です。これが window.innerHeight(ブラウザ画面の高さ)より小さくなった瞬間が、「要素が画面の下から見えた瞬間」になります。そこから - 100 としているのは、「画面に入ってからさらに100pxスクロールしたら発火させる」という微調整のためです。

まとめ

今回は getBoundingClientRect() を使って、よく使われる3つのUI機能を実装してみました。

単に「座標が取れる」と覚えるだけでなく、「ボタンの幅(width)を足して中心を求める」「画面の高さ(innerHeight)と比較してスクロール量を見る」といった計算の組み合わせを知っておくと、フロントエンドの表現力は格段に上がります!

現在はIntersection Observerなど便利なAPIも登場していますが、座標を自力で計算するこの基礎知識はどんな複雑なアニメーションを作る際にも必ず役立ちます。ぜひご自身のポートフォリオ作品などでも活用してみてください!

Prev
Next