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 width 문자열 200px 200px

표는 아래 예제의 CSS width 200px, padding 좌우 각 20px, border 각 5px, margin 각 10px 조건입니다. .width()는 box-sizing이 border-box여도 콘텐츠 너비를 반환합니다. .innerWidth()와 .outerWidth()는 각각 padding과 border를 포함하는 경계를 비교할 때 사용합니다.

box-sizing이 content-box면 CSS width는 콘텐츠 200px를 뜻하므로 테두리까지 200+40+10=250px입니다. border-box면 같은 CSS 200px 안에 padding 40px와 border 10px가 들어가 콘텐츠는 150px입니다. 두 모델에서 .width()의 의미가 바뀌는 것이 아니라, CSS width가 지정하는 범위가 달라집니다.

두 박스 모델을 비교하는 전체 예제

코드를 UTF-8의 width-demo.html로 저장해 브라우저로 엽니다. 인터넷으로 공식 jQuery 파일 4.0.0을 읽습니다. 별도 서버나 설치는 필요하지 않습니다. Windows Edge에서 jQuery 3.7.1·4.0.0으로 초기값, padding 변경과 복원, 소수 너비를 실행해 확인했습니다.

두 박스는 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을 20px로 되돌립니다. 초기 출력과 일치하는지 확인합니다.
  4. Toggle width 200 / 200.5를 누르면 각 너비 결과가 초기값보다 0.5 증가합니다. 반환값을 항상 정수라고 가정하지 않습니다.
  5. Reset은 두 박스의 width와 padding을 초기값으로 돌립니다. 다른 사이트의 전역 CSS를 변경하는 동작은 아닙니다.

숫자 반환값과 여러 요소 처리

인자 없이 호출한 너비 메서드는 선택 집합의 첫 요소를 읽습니다. 여러 요소의 너비가 모두 필요하면 .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 하나만 측정하며, 두 번째 줄은 각 요소의 콘텐츠 너비입니다. 선택에 맞는 요소가 없으면 이 예제의 jQuery 3.7.1·4.0.0에서는 undefined입니다.

200
200,150
undefined undefined undefined

값이 0이면 실제 너비가 0인 경우와 빈 선택을 구분합니다. 계산 전에 $element.length와 반환값을 확인하고, undefined에 숫자를 더해 NaN이 되는 상황을 피합니다. .css("width")는 단위를 가진 문자열이므로 바로 덧셈하면 숫자 계산 대신 문자열 연결이 될 수 있습니다.

이 글은 측정용 getter를 다룹니다. .width(200)처럼 숫자를 전달하면 너비를 설정하는 setter가 되므로, 읽기만 하려는 코드에서 인자를 넣지 않습니다. 소수 값을 무조건 parseInt로 잘라 쓰면 여러 요소를 합칠 때 오차가 누적될 수 있습니다.

측정 시점과 화면 너비를 구분하기

선택한 요소나 상위 요소가 display:none이면 너비 측정에 의존하지 않습니다. jQuery가 임시로 표시해 측정하려는 경우에도 신뢰성과 비용 문제가 있어, 실제로 배치된 뒤 읽는 편이 적합합니다. visibility:hidden은 레이아웃을 유지하는 다른 상태입니다. 이미지·웹폰트·동적 콘텐츠 때문에 레이아웃이 바뀐다면 준비 시점만이 아니라 해당 자원이 반영된 시점도 고려합니다.

CSS transform으로 줄이거나 회전한 화면상의 경계가 필요하면 getBoundingClientRect()를 검토합니다. 예제 content-box에 scale(0.5)를 적용해 비교하면 .outerWidth()는 레이아웃 테두리 너비 250이고, 화면상의 사각형 width는 125였습니다. 회전하면 사각형 경계의 의미도 달라지므로 두 API를 같은 값으로 가정하지 않습니다.

일반 요소의 innerWidth·outerWidth를 window나 document에 그대로 적용하지 않습니다. 창과 문서 너비는 $(window).width(), $(document).width() 등 해당 목적의 측정을 구분합니다. margin 포함값도 부모 안에서 차지하는 실제 배치 폭이나 형제 간 간격을 모든 레이아웃에서 설명하는 값은 아닙니다.

스크롤바가 있는 박스, border-collapse:collapse 표, 브라우저 확대와 반올림, 변동 레이아웃에서는 단순 합산만으로 끝내지 말고 해당 환경의 값을 확인합니다. 반복문에서 스타일 쓰기와 측정을 번갈아 하면 레이아웃 계산이 잦아질 수 있으므로, 필요한 읽기와 변경을 모아 처리하는 것도 검토합니다.