offsetWidth / offsetHeight とは何か
offsetWidth と offsetHeight は、要素の「見た目のサイズ」をピクセルで返すプロパティです。ここが重要です:border(枠)と padding(内側余白)を含み、margin(外側余白)は含みません。スクロールバーが要素の中に出ている場合、その幅・高さも含まれます。つまり「ユーザーが見ている箱の外側サイズ」をすぐに知るための値です。
何が含まれ、何が含まれないか(箱モデルとの関係)
含まれるものと含まれないものの整理
offsetWidth/offsetHeight の計算には、content(中身)+ padding + border +(必要なら)スクロールバーが含まれます。margin は含まれません。ここが重要です:CSS の box-sizing に関係なく、この“見た目の外側寸法”で返ってくるため、レイアウトや位置合わせのときに扱いやすい数字です。
box-sizing の影響の受け方
box-sizing: content-box と border-box の違いは「CSS の width/height に何が含まれるか」ですが、offsetWidth/offsetHeight は常に“見た目の外側寸法”なので、その違いを意識せずに使えます。ここが重要です:幅合わせ・重ね合わせ・ポップアップの位置計算などでは offset の値をベースにするとズレが少なくなります。
基本の使い方(要素のサイズ取得と応用)
要素の見た目のサイズを読む
<div id="box" style="width:160px; padding:16px; border:4px solid #333">内容</div>
<script>
console.log("offsetWidth:", box.offsetWidth); // 160 + 16*2 + 4*2 = 200
console.log("offsetHeight:", box.offsetHeight); // 内容の高さ + 16*2 + 4*2
</script>
HTMLここが重要です:CSS の width(160px)に padding と border が足されて返ります。高さはテキスト量で変動しますが、同じく padding と border を含みます。
横並びの位置合わせ(右端にポップアップを付ける)
<div id="wrap" style="position:relative">
<button id="btn" style="position:absolute; left:20px; top:20px">ボタン</button>
<div id="popup" style="position:absolute; display:none">ポップアップ</div>
</div>
<script>
btn.addEventListener("click", () => {
popup.style.left = (btn.offsetLeft + btn.offsetWidth) + "px";
popup.style.top = btn.offsetTop + "px";
popup.style.display = "block";
});
</script>
HTMLここが重要です:同じ親の中で位置合わせするなら、offsetLeft/Top と offsetWidth/Height の組み合わせが最短ルートです。右端にぴったり付けるときも計算が簡単です。
画面端のはみ出しを避ける補正
<script>
function placeRightOf(el, menu) {
const x = el.offsetLeft + el.offsetWidth;
const y = el.offsetTop;
const vw = el.offsetParent.clientWidth;
const mh = menu.offsetHeight;
const mw = menu.offsetWidth;
const nx = Math.min(x, vw - mw - 8); // 右端から8px残す
const ny = Math.max(0, Math.min(y, el.offsetParent.clientHeight - mh - 8));
menu.style.left = nx + "px";
menu.style.top = ny + "px";
}
</script>
HTMLここが重要です:親の見えている領域(clientWidth/clientHeight)とメニューの offsetWidth/Height を使って“はみ出し防止”ができます。
似た値との違い(clientWidth/Height・getBoundingClientRect)
clientWidth / clientHeight との違い
clientWidth/Height は「content+padding」を返し、border とスクロールバーは含みません。ここが重要です:より“内側の空間”が欲しいなら client を、外側の箱寸法が欲しいなら offset を使います。スクロール可能な要素でスクロールバーのスペースを考慮したい場合は offset が適任です。
getBoundingClientRect との違い
getBoundingClientRect() は画面(ビューポート)に対する位置とサイズを返します。width/height はサブピクセル(小数)になることがあり、スクロールやズームも反映されます。offsetWidth/Height は整数ピクセルで返されます。ここが重要です:画面座標での正確な重なり判定や交差判定には rect、レイアウト用の“ざっくり箱サイズ”には offset が向いています。
スクロールバー・ボーダー・表示状態の影響
スクロールバーを含むかどうか
overflow: auto などで要素内にスクロールバーが表示されていれば、その幅(一般に縦スクロールバーは幅、横スクロールバーは高さ)を offsetWidth/Height は含みます。ここが重要です:内側にコンテンツをぴったり並べたいとき、スクロールバー分の余白も考慮するなら offset を基準にすると安全です。
border がある親子レイアウト
border は offset の箱に含まれます。そのため、border の有無で“置ける位置”が変わります。ここが重要です:細かなピクセル合わせが必要なら、border/padding の有無を明示し、基準となる親の CSS を安定化しておきましょう。
非表示要素の注意点
display: none の要素は offsetWidth/Height が 0 になります。一時的に計測したい場合は、visibility: hidden(表示はしないがレイアウトに参加)で計測し、必要な値が得られたら元に戻す方法があります。ここが重要です:開閉式 UI の事前計測は表示状態が鍵です。
実践例(レスポンシブ計測、折りたたみアニメ、等分レイアウト)
レスポンシブで“収まるか”判定して省略記号に切り替える
<div id="title" style="max-width:240px; padding:8px; border:1px solid #ccc; white-space:nowrap; overflow:hidden; text-overflow:ellipsis"></div>
<script>
title.textContent = "とても長いタイトルが入ることを想定しています";
function fits() {
return title.scrollWidth <= title.offsetWidth; // 収まっているか
}
console.log("収まる?", fits());
</script>
HTMLここが重要です:scrollWidth(実際の必要幅)と offsetWidth(見た目の箱幅)を比較すれば、折り返しや省略の判定が簡単です。
折りたたみアニメの自然な高さ取得
<div id="panel" style="overflow:hidden; transition: height .25s ease"></div>
<script>
function openPanel(content) {
panel.textContent = content;
panel.style.height = "auto"; // 自然高さに
const h = panel.offsetHeight; // 見た目の高さを取得
panel.style.height = "0px";
requestAnimationFrame(() => panel.style.height = h + "px");
}
function closePanel() {
panel.style.height = "0px";
}
openPanel("テキスト\nテキスト\nテキスト");
</script>
HTMLここが重要です:コンテンツの自然な高さを offsetHeight で計測してからアニメに使うと、コンテンツ量に応じてスムーズに開閉できます。
等分レイアウトで“余り幅”を正確に配分
<div id="wrap" style="display:flex; gap:8px">
<div class="box" style="flex:1; padding:8px; border:1px solid #ccc">A</div>
<div class="box" style="flex:1; padding:8px; border:1px solid #ccc">B</div>
<div class="box" style="flex:1; padding:8px; border:1px solid #ccc">C</div>
</div>
<script>
function leftover() {
const used = Array.from(wrap.children).reduce((s, el) => s + el.offsetWidth, 0);
const total = wrap.clientWidth;
return total - used; // gapやborder込みの使用幅を差し引いた余り
}
console.log("余り幅:", leftover());
</script>
HTMLここが重要です:見た目に使っている幅を offsetWidth で合計すると、残余を正しく計算できます。微調整や最終アイテムの拡張に使えます。
よくある落とし穴と回避策
小数のズレに悩む
offsetWidth/Height は整数で、サブピクセルは切り捨てられます。ズームやスケーリングで正確な交差や重なり判定が必要なら、getBoundingClientRect の width/height(小数)を使いましょう。ここが重要です:粗い箱寸法なら offset、精密な画面寸法なら rect を選ぶのがコツです。
スクロールバーで期待値と違う
スクロールバーを含むため、内側の“実際に使える幅”は clientWidth/Height のほうが正確です。ここが重要です:内側レイアウトの計算では client、外側配置の計算では offset を使い分けます。
非表示状態で計測してしまう
display: none の要素は 0 を返します。開閉 UI の計測は、一時的に表示(または visibility: hidden)状態で行いましょう。ここが重要です:“見えている箱のサイズ”という定義に沿った計測が必要です。
位置とサイズをごちゃ混ぜに計算する
位置は offsetLeft/Top(または rect.left/top)、サイズは offsetWidth/Height(または rect.width/height)と、役割を分けて使います。ここが重要です:座標と寸法を混同するとズレの原因になります。基準(親か画面か)も常に意識しましょう。
まとめ
offsetWidth/offsetHeight は「要素の見た目の外側サイズ」を、padding・border・(必要なら)スクロールバー込みの整数ピクセルで返します。外側の配置や位置合わせ、はみ出し補正、自然高さの取得に向いており、内側のレイアウト計算なら clientWidth/Height、画面座標の精密計測なら getBoundingClientRect を使い分けます。表示状態・スクロールバー・box-model を意識して設計すれば、初心者でもズレの少ないレイアウトと気持ちよい UI を実装できます。
