43

In MDN's description of Element.clientWidth it says.

Note: I've since updated MDN according to @potatopeelings answer.

The Element.clientWidth property is the inner width of an element in pixels. It includes padding but not the vertical scrollbar (if present, if rendered), border or margin.

This property will round the value to an integer. If you need a fractional value, use element.getBoundingClientRect().

From this I understand that other than rounding clientWidth should be the same as getBoundingClientRect().width.

However I see that for many elements (where display is inline?) the clientWidth (and height) are zero, while the values returned by getBoundingClientRect seem correct.

Searching on stackoverflow brings some answers saying that this happens before the document is in a ready state but I see this all the time, not just when the page is loading.

This behaviour is consistent for all browsers I checked, where is it specified that this should be the behaviour?

Sample:

function str(name, width, height) {
  return name + ': (' + width + ', ' + height + ')';
}

function test() {
  var s = document.getElementById('s');
  var rect = s.getBoundingClientRect();
  document.getElementById('out').innerHTML =
    str('client', s.clientWidth, s.clientHeight) + '<br/>' +
    str('bounding', rect.width, rect.height);
}
 <span id="s">A span</span><br/> <button onclick="test()">Test</button>
<hr />
<div id="out"></div>
Community
  • 1
  • 1
Motti
  • 110,860
  • 49
  • 189
  • 262

3 Answers3

46

From the spec (http://www.w3.org/TR/cssom-view/#dom-element-clientwidth)

The clientWidth attribute must run these steps:

1.If the element has no associated CSS layout box or if the CSS layout box is inline, return zero.
...

potatopeelings
  • 40,709
  • 7
  • 95
  • 119
  • That's it! Do you have any idea why this is? – Motti Sep 07 '15 at 13:13
  • 1
    I believe it's been like that in the spec since quite some time - the earliest reference I could find is https://lists.w3.org/Archives/Public/www-style/2008Mar/0060.html (from 2008), but I'm pretty sure there would be older references and even older references if you went browser specific. – potatopeelings Sep 07 '15 at 14:14
  • 1
    This happened to me today. Though despite the element _having_ `display: block` clientHeight would return 0. That is because I was using `.innerHTML` (https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) in another place, another file even. And while doing so and HTML was added all other elements I measured would return 0 for their height. Once I wrapped the adding of HTML in the `DOMContentLoaded` event all works fine and real height of the element is returned. So be careful, when you add HTML to the DOM in another place, clientWidth and clientHeight will/might be 0. – lowtechsun May 08 '20 at 21:55
28

In addition to @potatopeelings's answer:

Inline elements have no intrinsic or specified dimensions. E.g. you cannot define a width for a span (unless you change it's display property).

Also clientWidth and getBoundingClientRect serve different purposes and may return different values. The latter also considers transforms and returns the dimensions of an element as it is actually rendered.

.class {
  transform: scale(0.5);
}

If clientWidth returned 1000 in this case then the width of getBoundingClientRect would be 500.

You can regard clientWidth as "how much space do I have available within the element" and getBoundingClientRect as "how much space does the element occupy on the screen". So in our case the element would have enough space to contain two 500px elements side by side and it would occupy 500px on the screen.

a better oliver
  • 26,330
  • 2
  • 58
  • 66
  • 3
    css lives in an ocean of semantic mess, so thank you for clarifying these two properties in how they can be perceived. – aaaaaa Sep 26 '16 at 18:11
1

The returned value from getBoundingClientRect() is a DOMRect object which is the union of the rectangles returned by getClientRects() for the element ; this method looks at the bounding box dimension even if the element is inline (it doesn't have the limit for inline elements clientWidth has - specs @potatopeelings links).

function str(name, width, height) {
  return name + ': (' + width + ', ' + height + ')';
}

function test() {
  var s = document.getElementById('s');
  var rect = s.getBoundingClientRect();
  document.getElementById('out').innerHTML =
    str('client', s.clientWidth, s.clientHeight) + '<br/>' +
    str('bounding', rect.width, rect.height);
}
#s{
  display: inline-block
}
<span id="s">A span</span><br/> <button onclick="test()">Test</button>
<hr />
<div id="out"></div>

check the difference when you change the display property to inline-block or block.

maioman
  • 18,154
  • 4
  • 36
  • 42