Understanding offsetWidth, clientWidth, scrollWidth and -Height, respectively
Asked Answered
M

6

542

There are several questions on StackOverflow regarding offsetWidth / clientWidth / scrollWidth (and -Height, respectively), but none give comprehensive explanation of what those values are.

Also, there are several sources on the web giving confusing or incorrect information.

Can you give a complete explanation including some visual hints? Also, how can those values be used to calculate scroll bar widths?

Merrygoround answered 11/1, 2014 at 15:26 Comment(0)
M
1162

The CSS box model is rather complicated, particularly when it comes to scrolling content. While the browser uses the values from your CSS to draw boxes, determining all the dimensions using JS is not straight-forward if you only have the CSS.

That's why each element has six DOM properties for your convenience: offsetWidth, offsetHeight, clientWidth, clientHeight, scrollWidth and scrollHeight. These are read-only attributes representing the current visual layout, and all of them are integers (thus possibly subject to rounding errors).

Let's go through them in detail:

  • offsetWidth, offsetHeight: The size of the visual box incuding all borders. Can be calculated by adding width/height and paddings and borders, if the element has display: block
  • clientWidth, clientHeight: The visual portion of the box content, not including borders or scroll bars , but includes padding . Can not be calculated directly from CSS, depends on the system's scroll bar size.
  • scrollWidth, scrollHeight: The size of all of the box's content, including the parts that are currently hidden outside the scrolling area. Can not be calculated directly from CSS, depends on the content.

CSS2 Box Model

Try it out: jsFiddle


Since offsetWidth takes the scroll bar width into account, we can use it to calculate the scroll bar width via the formula

scrollbarWidth = offsetWidth - clientWidth - getComputedStyle().borderLeftWidth - getComputedStyle().borderRightWidth

Unfortunately, we may get rounding errors, since offsetWidth and clientWidth are always integers, while the actual sizes may be fractional with zoom levels other than 1.

Note that this

scrollbarWidth = getComputedStyle().width + getComputedStyle().paddingLeft + getComputedStyle().paddingRight - clientWidth

does not work reliably in Chrome, since Chrome returns width with scrollbar already substracted. (Also, Chrome renders paddingBottom to the bottom of the scroll content, while other browsers don't)

Merrygoround answered 11/1, 2014 at 15:26 Comment(10)
For those seeking finer granularity than integers, use element.getBoundingClientRect() (see the note at developer.mozilla.org/en-US/docs/Web/API/Element.clientWidth)Williford
Note that depending on your layout, scrollWidth and scrollHeight can be really useful to get the size of your pseudo elements ::before and ::after.Works
Also, it would be useful to explain how do those relate to naturalWidth and naturalHeightTalos
why scrollHeight includes padding-bottom but scrollWidth does not include padding-rightBugger
clientWidth for document.documentElement.clientWidth is different as it seems to include the padding, borders, and marginKrick
From the clientWidth spec, if the element is the HTML or body element "return the viewport width excluding the size of a rendered scroll bar (if any).", so document.documentElement.clientWidth includes the padding/border/margin w3.org/TR/cssom-view/#dom-element-clientwidthKrick
I don't understand why offsetWidth can be calculated ? it includes the scrollbar width which depends on the systemIsoagglutinin
@OlivierBoissé How do you think anything ever gets drawn? (It's not like JS is guaranteeing you that all values will always be the same on all machines.)Overdraw
Note...: You need to parseFloat() the computed style properties...Overdraw
what about innerWidth/innerHeight & outerWidth/outerHeight?Inflection
P
97

I created a more comprehensive and cleaner version that some people might find useful for remembering which name corresponds to which value. I used Chrome Dev Tool's color code and labels are organized symmetrically to pick up analogies faster:

enter image description here

  • Note 1: clientLeft also includes the width of the vertical scroll bar if the direction of the text is set to right-to-left (since the bar is displayed to the left in that case)

  • Note 2: the outermost line represents the closest positioned parent (an element whose position property is set to a value different than static or initial). Thus, if the direct container isn’t a positioned element, then the line doesn’t represent the first container in the hierarchy but another element higher in the hierarchy. If no positioned parent is found, the browser will take the html or body element as reference


Hope somebody finds it useful, just my 2 cents ;)

Portsalut answered 26/8, 2017 at 16:16 Comment(4)
This is a nice clean diagram, but why omit padding?Acervate
@Acervate It makes use of Chrome Dev Tool's color code: greyish blue for content, greyish green for padding, dark beige for border, and light orange for margin.Portsalut
I'm not sure this is right... without labels for your colored areas it's hard to tell. You seem to be saying that clientWidth does not include padding... which is wrongInflect
@DonP As stated above, it follows Chrome Dev Tool's color convention (with lower opacity areas representing hidden areas in a browser). Here's an interactive version to clarify the clientWidth value: codepen.io/lual/pen/oNeydMz. The browser does consider the padding, but if the width of the element is set to a specific value and box-sizing is set border-box, it won't affect the final value. I may create a better version of the static graphic in the future though, indeed, some details aren't as clear as they could be.Portsalut
G
38

If you want to use scrollWidth to get the "REAL" CONTENT WIDTH/HEIGHT (as content can be BIGGER than the css-defined width/height-Box) the scrollWidth/Height is very UNRELIABLE as some browser seem to "MOVE" the paddingRIGHT & paddingBOTTOM if the content is to big. They then place the paddings at the RIGHT/BOTTOM of the "too broad/high content" (see picture below).

==> Therefore to get the REAL CONTENT WIDTH in some browsers you have to substract BOTH paddings from the scrollwidth and in some browsers you only have to substract the LEFT Padding.

I found a solution for this and wanted to add this as a comment, but was not allowed. So I took the picture and made it a bit clearer in the regard of the "moved paddings" and the "unreliable scrollWidth". In the BLUE AREA you find my solution on how to get the "REAL" CONTENT WIDTH!

Hope this helps to make things even clearer!

enter image description here

Gelasias answered 12/11, 2015 at 13:2 Comment(0)
S
19

There is a good article on MDN that explains the theory behind those concepts: https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model/Determining_the_dimensions_of_elements

It also explains the important conceptual differences between boundingClientRect's width/height vs offsetWidth/offsetHeight.

Then, to prove the theory right or wrong, you need some tests. That's what I did here: https://github.com/lingtalfi/dimensions-cheatsheet

It's testing for chrome53, ff49, safari9, edge13 and ie11.

The results of the tests prove that the theory is generally right. For the tests, I created 3 divs containing 10 lorem ipsum paragraphs each. Some css was applied to them:

.div1{
    width: 500px;
    height: 300px;
    padding: 10px;
    border: 5px solid black;
    overflow: auto;
}
.div2{
    width: 500px;
    height: 300px;
    padding: 10px;
    border: 5px solid black;
    box-sizing: border-box;
    overflow: auto;
}

.div3{
    width: 500px;
    height: 300px;
    padding: 10px;
    border: 5px solid black;
    overflow: auto;
    transform: scale(0.5);
}

And here are the results:

  • div1

    • offsetWidth: 530 (chrome53, ff49, safari9, edge13, ie11)

    • offsetHeight: 330 (chrome53, ff49, safari9, edge13, ie11)

    • bcr.width: 530 (chrome53, ff49, safari9, edge13, ie11)

    • bcr.height: 330 (chrome53, ff49, safari9, edge13, ie11)

    • clientWidth: 505 (chrome53, ff49, safari9)

    • clientWidth: 508 (edge13)

    • clientWidth: 503 (ie11)

    • clientHeight: 320 (chrome53, ff49, safari9, edge13, ie11)

    • scrollWidth: 505 (chrome53, safari9, ff49)

    • scrollWidth: 508 (edge13)

    • scrollWidth: 503 (ie11)

    • scrollHeight: 916 (chrome53, safari9)

    • scrollHeight: 954 (ff49)

    • scrollHeight: 922 (edge13, ie11)

  • div2

    • offsetWidth: 500 (chrome53, ff49, safari9, edge13, ie11)

    • offsetHeight: 300 (chrome53, ff49, safari9, edge13, ie11)

    • bcr.width: 500 (chrome53, ff49, safari9, edge13, ie11)

    • bcr.height: 300 (chrome53, ff49, safari9)

    • bcr.height: 299.9999694824219 (edge13, ie11)

    • clientWidth: 475 (chrome53, ff49, safari9)

    • clientWidth: 478 (edge13)

    • clientWidth: 473 (ie11)

    • clientHeight: 290 (chrome53, ff49, safari9, edge13, ie11)

    • scrollWidth: 475 (chrome53, safari9, ff49)

    • scrollWidth: 478 (edge13)

    • scrollWidth: 473 (ie11)

    • scrollHeight: 916 (chrome53, safari9)

    • scrollHeight: 954 (ff49)

    • scrollHeight: 922 (edge13, ie11)

  • div3

    • offsetWidth: 530 (chrome53, ff49, safari9, edge13, ie11)

    • offsetHeight: 330 (chrome53, ff49, safari9, edge13, ie11)

    • bcr.width: 265 (chrome53, ff49, safari9, edge13, ie11)

    • bcr.height: 165 (chrome53, ff49, safari9, edge13, ie11)

    • clientWidth: 505 (chrome53, ff49, safari9)

    • clientWidth: 508 (edge13)

    • clientWidth: 503 (ie11)

    • clientHeight: 320 (chrome53, ff49, safari9, edge13, ie11)

    • scrollWidth: 505 (chrome53, safari9, ff49)

    • scrollWidth: 508 (edge13)

    • scrollWidth: 503 (ie11)

    • scrollHeight: 916 (chrome53, safari9)

    • scrollHeight: 954 (ff49)

    • scrollHeight: 922 (edge13, ie11)

So, apart from the boundingClientRect's height value (299.9999694824219 instead of expected 300) in edge13 and ie11, the results confirm that the theory behind this works.

From there, here is my definition of those concepts:

  • offsetWidth/offsetHeight: dimensions of the layout border box
  • boundingClientRect: dimensions of the rendering border box
  • clientWidth/clientHeight: dimensions of the visible part of the layout padding box (excluding scroll bars)
  • scrollWidth/scrollHeight: dimensions of the layout padding box if it wasn't constrained by scroll bars

Note: the default vertical scroll bar's width is 12px in edge13, 15px in chrome53, ff49 and safari9, and 17px in ie11 (done by measurements in photoshop from screenshots, and proven right by the results of the tests).

However, in some cases, maybe your app is not using the default vertical scroll bar's width.

So, given the definitions of those concepts, the vertical scroll bar's width should be equal to (in pseudo code):

  • layout dimension: offsetWidth - clientWidth - (borderLeftWidth + borderRightWidth)

  • rendering dimension: boundingClientRect.width - clientWidth - (borderLeftWidth + borderRightWidth)

Note, if you don't understand layout vs rendering please read the mdn article.

Also, if you have another browser (or if you want to see the results of the tests for yourself), you can see my test page here: http://codepen.io/lingtalfi/pen/BLdBdL

Starshaped answered 29/9, 2016 at 6:31 Comment(0)
T
15

My personal cheatsheet, covering:

DOM element dimensions

  • .offsetWidth/.offsetHeight
  • .clientWidth/.clientHeight
  • .scrollWidth/.scrollHeight
  • .scrollLeft/.scrollTop
  • .getBoundingClientRect()

with small/simple/not-all-in-one diagrams :)


👁 see full-size: https://docs.google.com/drawings/d/1bOOJnkN5G_lBs3Oz9NfQQH1I0aCrX5EZYPY3mu3_ROI/edit?usp=sharing

Trudy answered 14/10, 2020 at 10:51 Comment(0)
S
3

client width/height and offset width/height calculation - a quick summary using a sample css style:

enter image description here

Ref: https://javascript.info/size-and-scroll

Stateless answered 26/10, 2021 at 6:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.