Search code examples
cssflexboxgetcomputedstyle

Why is the sum of the children's width greater than the parent's width?


Trying to set an horizontal scroll (using inline-flex) in I encountered the following problem :

Can't get a padding around a scrolling div. Trying to understand why, it appears that the simple addition of the widths of the children gives a number (far) bigger than the computed width of the parent.

https://jsfiddle.net/jniac/vv9zhcj3/

var divs = document.querySelectorAll('.content > div');
var widths = [], sum = 0;
for (var i = 0; i < divs.length; i++) {
  var div = div = divs[i]
  // offsetWidth & getComputedStyle give (almost) the same value
  var w = div.offsetWidth;
  //var w = parseFloat(getComputedStyle(div).width);
  widths.push(w);
  sum += w;
}

console.log('div width:', widths.join(', '));
console.log('sum (including padding):', sum + 200 + 'px');
console.log('computed style:', getComputedStyle(document.querySelector('.content')).width);
console.log('...can\'t get spacing to the right :\'(');
* {
  margin:0;
  box-sizing: border-box;
  font-family: courier;
  font-size: 14px;
}
.container {
  width: 100vw;
  height: 100vh;
}
.content {
  display: inline-flex;
  height: 100%;
  padding: 100px;
}
.content > div {
  height: 100%;
  padding: 20px;
  flex: none;
}
.A { background: red; }
.B { background: blue; }
.C { background: orange; }
.inside {
  height: 100%;
  padding: 40px;
  border: rgba(255,255,255,.75) 2px dashed;
  color: white;
  font-size: 90px;
}
<div class="container">
  <div class="content">
    <div class="A"><div class="inside">AA</div></div>
    <div class="B"><div class="inside">B</div></div>
    <div class="C"><div class="inside">Long-C</div></div>
    <div class="A"><div class="inside">Quite-Long-A</div></div>
    <div class="C"><div class="inside">CC</div></div>
  </div>
</div>

var w = parseFloat(getComputedStyle(div).width); 2062px > 1684.19px

The result is quite amazing: While the computed style give a small value for width, in fact the div is scrolling by a greater offset than the computed value (does scrolling use another evaluation of widths ?), which is still smaller than it should be: can't get a spacing to the right.


Solution

  • That's because the flex container is being sized using fit-content / shrink-to-fit algorithm. Since it overflows, what matters is the min-content contribution of the flex items.

    However, the flex items are being sized using their max-content as their hypothetical main size.

    You can usewhitespace: nowrap to make the min-content contributions be the same as the max-content.

    * {
      margin:0;
      box-sizing: border-box;
      font-family: courier;
      font-size: 14px;
    }
    .container {
      width: 100vw;
      height: 100vh;
    }
    .content {
      display: inline-flex;
      height: 100%;
      padding: 100px;
    }
    .content > div {
      height: 100%;
      padding: 20px;
      flex: none;
      white-space: nowrap;
    }
    .A { background: red; }
    .B { background: blue; }
    .C { background: orange; }
    .inside {
      height: 100%;
      padding: 40px;
      border: rgba(255,255,255,.75) 2px dashed;
      color: white;
      font-size: 90px;
    }
    <div class="container">
      <div class="content">
        <div class="A"><div class="inside">AA</div></div>
        <div class="B"><div class="inside">B</div></div>
        <div class="C"><div class="inside">Long-C</div></div>
        <div class="A"><div class="inside">Quite-Long-A</div></div>
        <div class="C"><div class="inside">CC</div></div>
      </div>
    </div>