【JavaScript】要素の幅を取得する方法|offsetWidth・clientWidth・getBoundingClientRectの違い

JavaScript

JavaScriptでHTML要素の幅を取得したいとき、使えるプロパティやメソッドは複数あります。offsetWidthclientWidthgetBoundingClientRect()getComputedStyle() — それぞれが返す値は微妙に異なり、どれを使うかで結果が変わることがあります。

この記事では、4つの取得方法の違いを図解・比較表付きで解説し、ResizeObserverによるリサイズ監視や、実務で頻出するパターン、陥りやすいミスまで体系的にまとめます。

この記事で学べること

  • offsetWidth / clientWidth / getBoundingClientRect / getComputedStyle の違い
  • 各メソッドが含む範囲(content・padding・border・scrollbar)
  • CSS transform 反映の有無と小数点精度の差
  • ウィンドウ幅・ビューポート幅の取得方法
  • ResizeObserver による要素サイズ監視
  • display:none や box-sizing で起きるミスと対策
スポンサーリンク

要素の幅を取得する4つの方法(早見表)

JavaScriptで要素の幅を取得するには、主に次の4つの方法があります。まずは全体像を把握しましょう。

方法 含まれる範囲 戻り値 主な用途
offsetWidth content + padding + border + scrollbar 整数(px) 要素の見た目の全幅
clientWidth content + padding 整数(px) 内部の描画領域
getBoundingClientRect() content + padding + border 小数(px) 正確なサイズ・位置
getComputedStyle() CSSの計算値(box-sizingに依存) 文字列(例: "200px") CSS指定値の確認

要素の幅に関するCSSボックスモデルの構成は、外側から margin > border > padding > content の順です。どの方法がどこまで含むかを理解することが、正しく幅を取得するカギです。

offsetWidth の使い方

offsetWidth は、要素のレイアウト上の全幅を整数値(ピクセル)で返す読み取り専用プロパティです。content + padding + border + スクロールバーの幅がすべて含まれます。

offsetWidth に含まれる範囲

視覚的に整理すると、以下のようなイメージです。

offsetWidth の範囲(イメージ)
|<------------ offsetWidth ----------->|
|  border | padding | content | padding | scrollbar | border |
|         |<--- clientWidth -->|         |           |        |

ここで重要なのは、offsetWidth にはスクロールバーの幅も含まれるという点です。clientWidth にはスクロールバーは含まれません。

基本的な使用例

HTML
<div id="box" style="width:200px; padding:20px; border:5px solid #333;">
  コンテンツ
</div>
JavaScript
const box = document.getElementById('box');

console.log(box.offsetWidth);
// content(200) + padding(20*2) + border(5*2) = 250
実行結果
250

box-sizing: border-box の場合

box-sizing: border-box を指定すると、width: 200px に padding と border が含まれるため、offsetWidth200 になります。

JavaScript
// box-sizing: border-box の場合
// width:200px に padding と border が含まれる
const box = document.getElementById('box');
console.log(box.offsetWidth); // 200

ポイント

offsetWidthbox-sizing に関係なく、要素が画面上で占める実際のピクセル幅を返します。content-box でも border-box でも、目に見える全幅が得られます。

clientWidth の使い方

clientWidth は、要素の内部の描画可能領域の幅を整数値(ピクセル)で返します。content + padding が含まれますが、border とスクロールバーは含まれません

clientWidth に含まれる範囲

clientWidth の範囲(イメージ)
|  border | padding | content | padding | scrollbar | border |
|         |<----- clientWidth ---->|         |           |        |

clientWidth = content + padding(左右)
※ border、scrollbar は含まない

基本的な使用例

JavaScript
const box = document.getElementById('box');

// width:200px, padding:20px, border:5px の場合
console.log(box.clientWidth);
// content(200) + padding(20*2) = 240

console.log(box.offsetWidth);
// content(200) + padding(20*2) + border(5*2) = 250
実行結果
clientWidth: 240
offsetWidth: 250

offsetWidth との違い

offsetWidthclientWidth の差は、border + スクロールバーの幅です。スクロールバーがない要素では border の幅だけが差になります。

JavaScript
const el = document.getElementById('scrollable');

// width:300px, padding:10px, border:2px, overflow:scroll の場合
console.log('offsetWidth:', el.offsetWidth);
// 300 + 10*2 + 2*2 = 324

console.log('clientWidth:', el.clientWidth);
// 300 + 10*2 - scrollbar(約17px) = 約303

// 差分 = border(左右) + scrollbar
const diff = el.offsetWidth - el.clientWidth;
console.log('差分:', diff); // border(4) + scrollbar(約17) = 約21

注意

スクロールバーの幅はOSやブラウザによって異なります。Windows の Chrome では約17pxですが、macOS ではオーバーレイスクロールバー(幅0px)がデフォルトです。そのため、offsetWidth - clientWidth の結果は環境によって変わります。

getBoundingClientRect() の使い方

getBoundingClientRect() は要素のサイズと位置DOMRect オブジェクトで返すメソッドです。幅だけでなく、top、left、height なども同時に取得できます。

返されるプロパティ一覧

プロパティ 説明
width 要素の幅(content + padding + border)
height 要素の高さ
top ビューポート上端から要素上端までの距離
right ビューポート左端から要素右端までの距離
bottom ビューポート上端から要素下端までの距離
left ビューポート左端から要素左端までの距離
x / y left / top と同じ値

基本的な使用例

JavaScript
const box = document.getElementById('box');
const rect = box.getBoundingClientRect();

console.log('幅:', rect.width);   // 250(小数点あり: 250.5 など)
console.log('高さ:', rect.height); // 要素の高さ
console.log('上:', rect.top);     // ビューポート上端からの距離
console.log('左:', rect.left);    // ビューポート左端からの距離

小数点以下の精度

getBoundingClientRect()小数点以下の値を返します。これは offsetWidth(整数に丸められる)との大きな違いです。

JavaScript
// 例: width: 33.33% の要素
const el = document.querySelector('.third');

console.log(el.offsetWidth);                          // 333(整数に丸められる)
console.log(el.getBoundingClientRect().width);  // 333.328125(正確な値)

CSS transform が反映される

getBoundingClientRect() は CSS の transformscalerotate など)が反映された後の値を返します。一方、offsetWidth は transform を無視します。

JavaScript
// <div id="scaled" style="width:200px; transform:scale(1.5);">
const el = document.getElementById('scaled');

console.log(el.offsetWidth);                          // 200(transform無視)
console.log(el.getBoundingClientRect().width);  // 300(200 * 1.5)

ポイント

アニメーションや transform: scale() を使っている要素の「画面上の見た目の幅」を正確に取得したい場合は、getBoundingClientRect() を使いましょう。

getComputedStyle() の使い方

getComputedStyle() は、要素に最終的に適用されているCSSの計算値を取得するメソッドです。幅は width プロパティで取得でき、文字列(例: "200px")として返されます。

基本的な使用例

JavaScript
const box = document.getElementById('box');
const styles = window.getComputedStyle(box);

console.log(styles.width);         // "200px"(文字列)
console.log(styles.paddingLeft);   // "20px"
console.log(styles.borderLeftWidth); // "5px"
console.log(styles.boxSizing);     // "content-box" or "border-box"

// 数値として使いたい場合は parseFloat で変換
const widthNum = parseFloat(styles.width);
console.log(widthNum); // 200

box-sizing の影響

getComputedStyle() で取得する width の値は、box-sizing によって意味が変わります。

box-sizing width の意味 例(width:200px, padding:20px, border:5px)
content-box content の幅のみ width => "200px"(contentのみ)
border-box content + padding + border width => "200px"(全体が200px)
JavaScript
// box-sizing による width の違いを確認
function getActualContentWidth(el) {
  const styles = window.getComputedStyle(el);
  const width = parseFloat(styles.width);

  if (styles.boxSizing === 'border-box') {
    // border-box: width には padding と border が含まれている
    const paddingX = parseFloat(styles.paddingLeft) + parseFloat(styles.paddingRight);
    const borderX = parseFloat(styles.borderLeftWidth) + parseFloat(styles.borderRightWidth);
    return width - paddingX - borderX;
  }

  // content-box: width はそのまま content の幅
  return width;
}

const el = document.getElementById('box');
console.log(getActualContentWidth(el));

注意

getComputedStyle() が返す width文字列です。計算に使う場合は必ず parseFloat() で数値に変換してください。また、display: none の要素では "auto" が返る場合があります。

各メソッドの違いを比較表で整理

ここまで解説した4つの方法を、項目別に比較します。

項目 offsetWidth clientWidth getBoundingClientRect() getComputedStyle()
content 含む 含む 含む box-sizingに依存
padding 含む 含む 含む box-sizingに依存
border 含む 含まない 含む box-sizingに依存
scrollbar 含む 含まない 含む 含まない
戻り値の型 整数 整数 小数 文字列
transform反映 されない されない される されない
display:none 0 0 0 "auto" の場合あり

使い分けの目安

  • 要素の見た目の全幅が欲しい → offsetWidth
  • 描画可能な内部領域が欲しい → clientWidth
  • 小数精度 + 位置情報が欲しい → getBoundingClientRect()
  • CSSの指定値を確認したい → getComputedStyle()

実務でよく使うパターン

ここからは、実際の開発で頻出する「幅の取得」パターンを紹介します。

ウィンドウ幅・ビューポート幅の取得

画面全体の幅を取得する方法は複数あり、それぞれ意味が異なります。

JavaScript
// ウィンドウの外側の幅(ブラウザウィンドウ全体)
console.log(window.outerWidth);

// ウィンドウの内側の幅(スクロールバーを含む)
console.log(window.innerWidth);

// ビューポートの幅(スクロールバーを含まない)
console.log(document.documentElement.clientWidth);

// ページ全体の幅(スクロール可能な領域を含む)
console.log(document.documentElement.scrollWidth);
プロパティ スクロールバー 説明
window.outerWidth ブラウザウィンドウの外枠を含む幅
window.innerWidth 含む ビューポート + スクロールバー
documentElement.clientWidth 含まない CSSメディアクエリと同じ幅
documentElement.scrollWidth 含まない 横スクロールを含むページ全体の幅

ポイント

レスポンシブ対応のブレークポイント判定には document.documentElement.clientWidth を使いましょう。CSSの @media クエリと同じ幅を返すため、CSS とJavaScript の判定を一致させることができます。

レスポンシブ対応の分岐処理

画面幅に応じてJavaScriptの処理を切り替える実装パターンです。

JavaScript
// 方法1: documentElement.clientWidth を使う
function isMobile() {
  return document.documentElement.clientWidth < 768;
}

// 方法2: matchMedia を使う(推奨)
const mql = window.matchMedia('(max-width: 767px)');

if (mql.matches) {
  console.log('モバイル表示');
}

// 画面幅の変化を監視
mql.addEventListener('change', (e) => {
  if (e.matches) {
    console.log('モバイル幅になりました');
  } else {
    console.log('デスクトップ幅になりました');
  }
});

レスポンシブ判定には window.matchMedia() が推奨されます。CSSメディアクエリと完全に同じ条件で判定でき、change イベントでブレークポイントの切り替わりを効率的に監視できます。

ResizeObserver で要素のリサイズを監視

ResizeObserver を使うと、特定の要素のサイズ変更をリアルタイムに検知できます。ウィンドウリサイズだけでなく、CSSアニメーションやコンテンツ変更によるサイズ変化も捕捉します。

JavaScript
const box = document.getElementById('box');

const observer = new ResizeObserver((entries) => {
  for (const entry of entries) {
    // contentRect: padding を除いたコンテンツ領域
    console.log('contentRect.width:', entry.contentRect.width);

    // borderBoxSize: border + padding + content(推奨)
    if (entry.borderBoxSize) {
      const bbs = entry.borderBoxSize[0];
      console.log('borderBoxSize.inlineSize:', bbs.inlineSize);
    }
  }
});

// 監視開始
observer.observe(box);

// 監視停止(不要になったら)
// observer.unobserve(box);
// observer.disconnect();
プロパティ 説明
contentRect.width コンテンツ領域の幅(paddingを含まない)
contentBoxSize[0].inlineSize content の幅(論理プロパティ)
borderBoxSize[0].inlineSize border + padding + content の幅(論理プロパティ)

スクロールバーの幅を計算する

スクロールバーの幅はブラウザやOSによって異なるため、動的に計算するのがベストプラクティスです。

JavaScript
// 方法1: ページのスクロールバー幅
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
console.log('スクロールバー幅:', scrollbarWidth, 'px');

// 方法2: 要素を使って正確に測定
function getScrollbarWidth() {
  const outer = document.createElement('div');
  outer.style.cssText = 'overflow:scroll;width:100px;height:100px;position:absolute;top:-9999px;';
  document.body.appendChild(outer);

  const width = outer.offsetWidth - outer.clientWidth;
  document.body.removeChild(outer);

  return width;
}

console.log(getScrollbarWidth()); // Windows: 17, macOS: 0(通常)

よくあるミスと注意点

要素の幅を取得する際に、初心者がハマりやすいポイントをまとめます。

display:none の要素は幅が取得できない

display: none が設定された要素は、レイアウトから完全に除外されるため、offsetWidthclientWidth0 を返します。

JavaScript
// display:none の要素
const hidden = document.getElementById('hidden-box');
hidden.style.display = 'none';

console.log(hidden.offsetWidth);  // 0
console.log(hidden.clientWidth);  // 0

// 解決策1: visibility:hidden を使う(レイアウト上のスペースは確保される)
hidden.style.display = '';
hidden.style.visibility = 'hidden';
console.log(hidden.offsetWidth);  // 正しい値が取得できる

// 解決策2: 一時的に表示して測定
function getHiddenElementWidth(el) {
  const originalStyles = {
    display: el.style.display,
    visibility: el.style.visibility,
    position: el.style.position
  };

  el.style.display = 'block';
  el.style.visibility = 'hidden';
  el.style.position = 'absolute';

  const width = el.offsetWidth;

  el.style.display = originalStyles.display;
  el.style.visibility = originalStyles.visibility;
  el.style.position = originalStyles.position;

  return width;
}

DOMContentLoaded のタイミングに注意

DOMの構築が完了する前にスクリプトが実行されると、要素がまだ存在しないため null が返ります。

JavaScript(NG例)
// <head> 内で実行するとエラー
const box = document.getElementById('box');
console.log(box.offsetWidth); // TypeError: Cannot read properties of null
JavaScript(OK例)
// 方法1: DOMContentLoaded を使う
document.addEventListener('DOMContentLoaded', () => {
  const box = document.getElementById('box');
  console.log(box.offsetWidth); // 正しく取得できる
});

// 方法2: <body> の末尾に <script> を配置
// 方法3: defer 属性を使う
// <script src="app.js" defer></script>

注意

DOMContentLoaded はDOMツリーの構築完了を待ちますが、画像やiframe の読み込みは待ちません。画像サイズに依存するレイアウトの幅を取得する場合は、window.addEventListener('load', ...) を使うか、対象の画像に width / height 属性を明示してください。

box-sizing による差異に注意

getComputedStyle() で取得した width を使って幅の計算をする場合、box-sizing の値を確認しないと意図しない結果になります。

JavaScript
// よくある間違い: box-sizing を考慮していない
function getTotalWidth(el) {
  const styles = window.getComputedStyle(el);
  const width = parseFloat(styles.width);
  const paddingX = parseFloat(styles.paddingLeft) + parseFloat(styles.paddingRight);
  const borderX = parseFloat(styles.borderLeftWidth) + parseFloat(styles.borderRightWidth);

  if (styles.boxSizing === 'border-box') {
    // width に padding + border が含まれているので足さない
    return width;
  }

  // content-box: width + padding + border
  return width + paddingX + borderX;
}

ポイント

box-sizing の影響を受けずに要素の全幅を取得したいなら、offsetWidthgetBoundingClientRect().width を使うのが最もシンプルです。getComputedStyle() は CSS の指定値を細かく調べたい場合にだけ使いましょう。

インライン要素では offsetWidth が 0 になる

<span><a> などのインライン要素は、デフォルトでは offsetWidth0 になります。幅を取得するには display を変更するか、getBoundingClientRect() を使います。

JavaScript
// <span id="label">テキスト</span>
const label = document.getElementById('label');

console.log(label.offsetWidth); // 0(インライン要素)

// getBoundingClientRect() ならインライン要素でも取得可能
console.log(label.getBoundingClientRect().width); // テキストの幅

// または display を変更する
label.style.display = 'inline-block';
console.log(label.offsetWidth); // テキストの幅が取得できる

よくある質問(FAQ)

Q. offsetWidthとclientWidthとgetBoundingClientRect().widthの違いは何ですか?
A. offsetWidthはボーダーを含む幅(paddingとborderを含む)。clientWidthはパディングを含むがボーダーは含まない幅。getBoundingClientRect().widthはCSSのtransformによる変形も反映した実際の表示幅。正確な実際のサイズが必要なときはgetBoundingClientRectが最も信頼性が高いです。
Q. 要素が非表示(display:none)のとき、幅の取得ができません。どうすればよいですか?
A. display:noneだとレイアウトが行われず0が返ります。一時的にvisibility:hiddenposition:absolute; opacity:0にしてdisplayをblockにしてから計測する方法があります。またはResizeObserverを使って表示時に幅を取得することも可能です。
Q. Windowのリサイズに応じてリアルタイムで要素の幅を取得するにはどうすればよいですか?
A. window.addEventListener("resize", callback)でウィンドウリサイズを検知し幅を再取得できます。頻繁すぎる呼び出しを防ぐためにthrottleやdebounceを使うことを推奨します。より効率的な方法としてResizeObserverAPI(IE非対応だが現代ブラウザ全対応)があります。

まとめ

JavaScriptで要素の幅を取得する4つの方法を解説しました。最後に、選び方の指針をまとめます。

やりたいこと おすすめのメソッド
要素のレイアウト上の全幅を取得したい offsetWidth
border やスクロールバーを除いた内部領域の幅が欲しい clientWidth
小数精度で正確な幅・位置が欲しい / transform 後の値が欲しい getBoundingClientRect()
CSS で指定された width の値を確認したい getComputedStyle()
ビューポートの幅(レスポンシブ判定) documentElement.clientWidth / matchMedia()
要素のサイズ変化をリアルタイム監視したい ResizeObserver

それぞれのメソッドが何を含むか・何を含まないかを理解しておけば、迷うことはありません。迷ったら、まずは offsetWidth(全幅)か getBoundingClientRect().width(正確な全幅)を使い、必要に応じて他のメソッドに切り替えるのがおすすめです。

この記事のまとめ

  • offsetWidth = content + padding + border + scrollbar(整数、transform 無視)
  • clientWidth = content + padding(整数、border とスクロールバーを除外)
  • getBoundingClientRect().width = content + padding + border(小数、transform 反映)
  • getComputedStyle().width = CSS計算値(文字列、box-sizing に依存)
  • display:none の要素は幅 0 になる(visibility:hidden で回避)
  • ResizeObserver で要素サイズの変化をリアルタイム監視できる
  • レスポンシブ判定には matchMedia() が最も信頼性が高い