jQuery / width・innerWidth・outerWidthで横幅を取得する

jQueryの.width()は内容の幅、.innerWidth()は左右padding込み、.outerWidth()は左右borderまで含む幅です。左右marginも含めるには.outerWidth(true)を使います。戻り値はpx換算の数値で、CSS width文字列とは異なります。

測定する境界を選ぶ

CSSボックスモデルは内容の外側にpadding、border、marginを置きます。目的に必要な境界を選びます。大きい値ほど正確という意味ではありません。

呼び出し 含む範囲 content-box例 border-box例
.width() 内容 200 150
.innerWidth() 内容 + padding 240 190
.outerWidth() 内容 + padding + border 250 200
.outerWidth(true) 上記 + margin 270 220
.css("width") 計算済みCSS文字列 200px 200px

CSS width 200px、左右padding各20px、border各5px、margin各10pxの条件です。.width()はborder-boxでも内容を返し、.innerWidth()と.outerWidth()はそれぞれの境界まで含めます。

box-sizingがcontent-boxなら外側は200+40+10=250です。border-boxはCSS 200の中にpaddingとborderを含めるので内容150です。.width()の意味ではなくCSS指定範囲が変わります。

比較できる完全な例

全コードをUTF-8のwidth-demo.htmlに保存しブラウザーで開きます。インターネットから公式jQueryファイル4.0.0を読み、サーバーや導入は不要です。初期値、padding変更と復元、小数幅をWindows Edge、3.7.1・4.0.0で実行しました。

開始条件はbox-sizingだけが異なります。.caseは狭い画面で局所的な横スクロールを許可します。出力のmarginは余白単独ではなく.outerWidth(true)の合計です。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Compare jQuery width measurements</title>
  <script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
  <style>
    body { font: 16px/1.6 sans-serif; max-width: 760px; margin: 20px; }
    button { font: inherit; margin: 4px; }
    .case { overflow-x: auto; background: #f4f7f8; margin: 12px 0; }
    .box { width: 200px; padding: 20px; border: 5px solid teal; margin: 10px; background: #fff; }
    #content { box-sizing: content-box; }
    #border { box-sizing: border-box; }
    pre { white-space: pre-wrap; overflow-wrap: anywhere; }
  </style>
</head>
<body>
  <button type="button" id="padding">Toggle padding 20 / 40</button>
  <button type="button" id="fraction">Toggle width 200 / 200.5</button>
  <button type="button" id="reset">Reset</button>
  <div class="case"><div class="box" id="content">content-box</div></div>
  <div class="case"><div class="box" id="border">border-box</div></div>
  <pre id="result" aria-live="polite"></pre>
  <script>
    $(function () {
      let padding = 20, width = 200;
      function report() {
        const lines = [];
        $(".box").each(function () {
          const $box = $(this);
          lines.push(`${this.id}: css=${$box.css("width")}; width=${$box.width()}; inner=${$box.innerWidth()}; outer=${$box.outerWidth()}; margin=${$box.outerWidth(true)}`);
        });
        $("#result").text(lines.join("\n"));
      }
      $("#padding").on("click", function () {
        padding = padding === 20 ? 40 : 20;
        $(".box").css("padding", padding + "px"); report();
      });
      $("#fraction").on("click", function () {
        width = width === 200 ? 200.5 : 200;
        $(".box").css("width", width + "px"); report();
      });
      $("#reset").on("click", function () {
        padding = 20; width = 200;
        $(".box").css({ padding: "20px", width: "200px" }); report();
      });
      report();
    });
  </script>
</body>
</html>

初期結果です。同じCSS幅でも内容と境界の測定値が異なります。

content: css=200px; width=200; inner=240; outer=250; margin=270
border: css=200px; width=150; inner=190; outer=200; margin=220
  1. Toggle padding 20 / 40でcontent-boxはwidth=200のまま、inner=280、outer=290、margin=310です。
  2. border-boxは外枠200を維持し、width=110、inner=190、outer=200、margin=220で内容が縮みます。
  3. 再度押してpaddingを20へ戻し初期結果を確認します。
  4. Toggle width 200 / 200.5で各幅が0.5増えます。整数とは限りません。
  5. Resetは両方のwidthとpaddingを戻し、全体CSSは変えません。

数値と複数要素

引数なしのgetterは最初の一致要素を測定します。全要素には.each()や.map()を使います。Reset後に開発者ツールで実行します。

console.log($("#content, #border").width());
console.log($(".box").map(function () { return $(this).width(); }).get().join(","));
console.log($(".missing").width(), $(".missing").innerWidth(), $(".missing").outerWidth());

最初はcontent-boxだけ、次は両方の内容幅です。空の選択は検証した3.7.1・4.0.0でundefinedです。

200
200,150
undefined undefined undefined

実幅0と空集合を区別し、lengthと戻り値を確認してundefinedからNaNになる計算を避けます。.css("width")は単位付き文字列なので加算が文字結合になる場合があります。parseIntで小数を常に切り捨てると複数幅の合計に誤差が出ます。

ここではgetterを扱います。.width(200)のように引数を渡すと設定用setterになるため、測定だけなら渡しません。

測定時点と見える境界

自身や祖先がdisplay:noneのときの寸法には依存せず、実際に配置してから測ります。一時表示による測定は信頼性と負荷に注意が必要です。visibility:hiddenは配置を維持する別状態です。画像、フォント、動的内容が準備後に変化する時点も考慮します。

CSS transform後の境界ならgetBoundingClientRect()を検討します。例のcontent-boxをscale(0.5)にすると.outerWidth()=250、画面の矩形幅=125でした。回転では囲む矩形の意味も変わり、両APIは同じではありません。

通常要素用のinnerWidth・outerWidthをwindowやdocumentへそのまま使わず、目的に合う.width()で測ります。margin込み合計も全配置で親内の占有幅や兄弟間隔を表すわけではありません。

スクロールバー、結合表罫線、ズーム、丸め、変動配置は実環境で確認します。反復中にスタイル変更と測定を交互にすると配置計算が増えるため、必要な読み取りと変更をまとめる方法も検討します。