6

Why does the element in the following example collapse in on itself?

Even though the element is set to box-sizing: border-box, its border will remain, yet the element loses all of its width as soon as it breaches the perimeter of its parent.

What is going on?

CODEPEN

let   t = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus at velit commodo, facilisis ex vitae, viverra tellus.'
let   c = 0
const e = document.getElementById('statement-container')
const i = setInterval(function(){
  if (t[c] !== ' ') e.innerHTML += t[c]
  else e.innerHTML += ' '
  c++
  if (c >= t.length) clearInterval(i)
}, 100)
* {
  box-sizing : border-box;
}

body {
  width       : 100vw;
  height      : 100vh;
  margin      : 0;
}

section:first-child {
  width       : 40vw;
  height      : 100vh;
  position    : relative;
  left        : 30vw;
  display     : flex;
  border      : solid blue 1px;
}

#statement-container, #caret {
  height      : 15%;
  position    : relative;
  top         : calc(50% - 7.5%);
}

#statement-container {
  z-index     : 98;
  font-family : beir-bold;
  font-size   : 6.5vmin;
  color       : red;
}

#caret {
  z-index     : 99;
  width       : 10%;
  border      : solid green 5px;
  background  : orange;
}
<body>
  <section>
    <div id="statement-container"></div>
    <div id="caret"></div>
  </section>
</body>
oldboy
  • 5,729
  • 6
  • 38
  • 86
  • 2
    I made the text longer to fully reproduce it, and rolled back the edits of @DemoDemo, because they basically broke the behavior that the question seems to be about... – GolezTrol Oct 09 '17 at 06:40

3 Answers3

5

Why does the element in the following example collapse in on itself?

As you are using display: flex on the section, its children becomes flex items.

Flex items has a property called flex, which is shorthand for flex-grow, flex-shrink and flex-basis, which control how the item size itself based on available space and its content.

The flex-basis, in this case in a flex row direction, which is the default, controls the items width.

The default value is flex: 0 1 auto which mean it won't grow (0) beyond its content, it is allowed to shrink (1) and its flex-basis (auto) size it according to its content.

Since in this case width and flex-basis do the same, here giving the caret an set width of 3%, it will keep that width as long as there is enough space, and won't grow, but when the space becomes negative (items won't fit anymore) it will start shrink down to its content width, which it doesn't have any, hence its content area collapse completely.


One solution would be to tell it to not be allowed to shrink, by changing flex-shrink to 0.

let   t = 'Nothing is completely perfect.'
let   c = 0
const e = document.getElementById('statement-container')
const i = setInterval(function(){
  if (t[c] !== ' ') e.innerHTML += t[c]
  else e.innerHTML += '&nbsp;'
  c++
  if (c >= t.length) clearInterval(i)
}, 100)
* {
  box-sizing : border-box;
}

body {
  width  : 100vw;
  height : 100vh;
  margin : 0;
}

section:first-child {
  width    : 40vw;
  height   : 100vh;
  position : relative;
  left     : 30vw;
  display  : flex;
  border   : solid blue 1px;
}

#statement-container, #caret {
  height      : 15%;
  position    : relative;
  top         : calc(50% - 7.5%);
}

#statement-container {
  font-family : beir-bold;
  font-size   : 15vmin;
  color       : red;
}

#caret {
  flex-shrink : 0;                             /* added  */
  width       : 5%;
  border      : solid green 5px;
  background  : orange;
}
  <section>
    <div id="statement-container"></div>
    <div id="caret"></div>
  </section>
Asons
  • 84,923
  • 12
  • 110
  • 165
  • `flex: 0 1 auto` as a default value is obviously browser specific, correct? – oldboy Oct 10 '17 at 08:41
  • @LGSon: Why `#statement-container` did not shrink? – Duannx Oct 10 '17 at 09:36
  • @Duannx Because a flex item by default will never be smaller than its _content_, which `statement-container` has and `caret` has not. If you add `min-width: 0` to the `statement-container` rule, which defaults to `auto`, it will also shrink, but overflow the text, which `overflow: hidden` would prevent, and can be used instead of `min-width: 0`. – Asons Oct 10 '17 at 09:56
  • @LGSon: If you add content `#caret`. It still shrink and `#statement-container` still does not. – Duannx Oct 10 '17 at 10:06
  • @Duannx If you show me a fiddle, or similar, that doesn't work as you expect, I'll let you know what does what. – Asons Oct 10 '17 at 10:09
  • @LGSon: Here is the [fiddle](https://jsfiddle.net/abowj8jo/1/). My expectation is two items shrink together, not only `#caret` – Duannx Oct 10 '17 at 10:20
  • @Duannx That is because it add a ` ` (`else e.innerHTML += ' '`) between the characters instead of a space in the script, where the `caret` has space ... this one use space: https://jsfiddle.net/abowj8jo/3/ – Asons Oct 10 '17 at 10:28
  • 1
    @LGSon: Yeah. Pertfect. Thank you very much. Upvoted your answer – Duannx Oct 10 '17 at 10:32
2

You can set min-width: 10%; the element calculated width after going outside the parent is 0 because the statement container is taking 100% of the available space. As stated here:

The min-width CSS property sets the minimum width of an element. It prevents the used value of the width property from becoming smaller than the value specified for min-width.

let   t = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus at velit commodo, facilisis ex vitae, viverra tellus.'
let   c = 0
const e = document.getElementById('statement-container')
const i = setInterval(function(){
  if (t[c] !== ' ') e.innerHTML += t[c]
  else e.innerHTML += '&nbsp;'
  c++
  if (c >= t.length) clearInterval(i)
}, 100)
* {
  box-sizing : border-box;
}

body {
  width  : 100vw;
  height : 100vh;
  margin : 0;
}

section:first-child {
  width    : 40vw;
  height   : 100vh;
  position : relative;
  left     : 30vw;
  display  : flex;
  border   : solid blue 1px;
}

#statement-container, #caret {
  height      : 15%;
  position    : relative;
  top         : calc(50% - 7.5%);
}

#statement-container {
  z-index     : 98;
  font-family : beir-bold;
  font-size   : 6.5vmin;
  color       : red;
}

#caret {
  z-index     : 99;
  width       : 10%;
  border      : solid green 5px;
  background  : orange;
  min-width: 10%;
}
<body>
  <section>
    <div id="statement-container"></div>
    <div id="caret"></div>
  </section>
</body>
Vanojx1
  • 5,574
  • 2
  • 23
  • 37
  • interesting. i'll upvote your answer, simply because it's additive, but it doesn't really answer the question. the question was **why** does it happen? – oldboy Oct 09 '17 at 06:46
  • i completely understand `min-width`, but what i don't understand is that the element's own width is set to 10% of **its parent** width. so, again, the question is why does the element lose its width if the widthof the parent element doesn't change? – oldboy Oct 09 '17 at 06:49
  • The caret height is 15% of the parent height, and that 15% its always available. The width in this case its calculated with `width = "parent width " - "statement container width"`and the result will be 0 after the "statement container width" reach the "parent width". To check this try to switch the position of the caret with the statement container, the caret width will be always 10% because nothing BEFORE is taking space. – Vanojx1 Oct 09 '17 at 06:59
  • nono, i completely understand what is happening. What I'm asking is why does the width of `statement-container` effect its width at all since its width is set to **`10%` of its parent's width**?? I'm assuming it has something to do with the way `flex` elements are regulated or behave. – oldboy Oct 09 '17 at 07:03
  • 2
    It's because of `display:flex` of the parent. Non-wrappable (by default) flex container tries to fit all its children (which become flex items) into a row inside it, shrinking some of these items if necessary as much as their `flex-shrink` and `flex-basis` values allow. By default, `flex-shrink` is `1` and `flex-basis` is `auto`, which basically means "by content". Since `.caret` has no content, it shrinks to zero. – Ilya Streltsyn Oct 09 '17 at 07:20
  • 2
    @Ilya Streltsyn: I encourage you to post that as a separate answer. – BoltClock Oct 09 '17 at 07:24
  • @BoltClock Since this answer doesn't correctly explains why, I posted an answer, and first now read up fully on these comments :) ... and there it was, though I laid mine out some more – Asons Oct 09 '17 at 10:31
-3

let   t = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus at velit commodo, facilisis ex vitae, viverra tellus.'
let   c = 0
const e = document.getElementById('statement-container')
const i = setInterval(function(){
  if (t[c] !== ' ') e.innerHTML += t[c]
  else e.innerHTML += '&nbsp;'
  c++
  if (c >= t.length) clearInterval(i)
}, 100)
* {
  box-sizing : border-box;
}

body {
  width  : 100vw;
  height : 100vh;
  margin : 0;
}

section:first-child {
  width    : 40vw;
  height   : 100vw;
  position : relative;
  left     : 30vw;
  display  : flex;
  border   : solid blue 1px;
}

#statement-container, #caret {
  height      : 15%;
  position    : relative;
  top         : calc(50% - 7.5%);
}

#statement-container {
  z-index     : 98;
  font-family : beir-bold;
  font-size   : 6.5vmin;
  color       : red;
}

#caret {
  z-index     : 99;
  width       : 10%;
  border      : solid green 5px;
  background  : orange;
}
<body>
  <section>
    <div id="statement-container"></div>
    <div id="caret"></div>
  </section>
</body>
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
Demo Demo
  • 82
  • 5