问题

有一个 flex box,宽度是 400px,里面有一个 item,item 中有一段很长的文 字,文字不换行。

由于外层盒子设置了宽度,预期是父盒子宽度能限制住 item,避免溢出。

但实际的表现是,文本没有隐藏显示,而是按照文本长度,溢出了盒子。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  <!DOCTYPE html>
  <html>
    <head>
      <style>
        .container {
            display: flex;
            height: 200px;
            width: 400px;
            background: yellow;
        }
        .item {}
        .text {
            white-space: nowrap;
            text-overflow: ellipsis;
            overflow: hidden;
        }
      </style>
    </head>

    <body>
      <div class="container">
        <div class="item">
          <p class="text">
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
          </p>
        </div>
      </div>
    </body>
  </html>

/post/flex-box-with-overflow/problem.png

非 flex 布局的表现

如果去除 flex 布局,用默认的 display:block 的盒子, 文本是会受限于外层 容器宽度,从而溢出隐藏显示。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  <!DOCTYPE html>
  <html>
    <head>
      <style>
        .container {
-            display: flex;
            height: 200px;
            width: 400px;
            background: yellow;
        }
        .item {}
        .text {
            white-space: nowrap;
            text-overflow: ellipsis;
            overflow: hidden;
        }
      </style>
    </head>

    <body>
      <div class="container">
        <div class="item">
          <p class="text">
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
          </p>
        </div>
      </div>
    </body>
  </html>

/post/flex-box-with-overflow/display-block.png

解决方案

给 flex item 设置 overflow 属性

1
2
3
.item {
+ overflow: auto;
}

/post/flex-box-with-overflow/overflow.png

给 flex item 设置 width: 0 & flex-grow: 1

1
2
3
4
.item {
+ flex-grow: 1;
+ width: 0;
}

/post/flex-box-with-overflow/width.png

给 flex item 设置 min-width: 0

1
2
3
.item {
+ min-width: 0;
}

/post/flex-box-with-overflow/min-width.png

原因

TL;DR

会溢出是因为在这个例子中,flex item 的 最小尺寸 是文本的长度,即使 flex item 可以缩小也只能缩小到文本长度,无法避免溢出。

通过设置 overflow 属性,可以让最小宽度变为 0,从而让 flex item 可以缩小到容器内而不溢出;

设置 min-width:0 同理,可以让 flex item 可以缩小到很小,避免溢出;

设置 width:0 则也会影响最小尺寸,同时也设置了 flex item 的宽度,但因为 宽度为 0,还需要设置 flex-grow:1 ,让 flex item 延伸容器剩余空间。

详细分析

当 flex item 没有设置 flex 属性时,默认值是 flex: initial ,相当于 flex: 0 1 auto,

也即 flex-grow: 0; flex-shrink: 1: flex-basic: auto;

默认值的效果是,根据 item 的 width/height 来设置尺寸,如果 width/height 没有指定,则会根据内容去计算尺寸。

item 无法延伸尺寸,但可以缩小到它的 最小尺寸

而最小尺寸的计算,在 4.5. Automatic Minimum Size of Flex Items 中有描述:

To provide a more reasonable default minimum size for flex items, the used value of a main axis automatic minimum size on a flex item that is not a scroll container is a content-based minimum size; for scroll containers the automatic minimum size is zero, as usual.

也就是说,如果 flex item 是一个滚动容器(scroll container,即设置了 overflow 属性),那么最小宽度就是 0。

所以,当设置了 overflow 后,flex item 可以尽可能地缩小直到刚好填满 flex container。

借助 firefox,可以看此时的尺寸如下:

/post/flex-box-with-overflow/layout-overflow.png

如果 不是 可滚动元素,有以下描述:

In general, the content-based minimum size of a flex item is the smaller of its content size suggestion and its specified size suggestion.

However, if the box has an aspect ratio and no specified size, its content-based minimum size is the smaller of its content size suggestion and its transferred size suggestion.

If the box has neither a specified size suggestion nor an aspect ratio, its content-based minimum size is the content size suggestion.

specified size suggestion 的定义:

If the item’s computed main size property is definite, then the specified size suggestion is that size (clamped by its max main size property if it’s definite). It is otherwise undefined.

根据 main size property,只要给 flex item 设置 width,或者 min-width, 那么从这些值上去计算最小尺寸。

所以可以通过设置 min-width:0 ,让 flex item 可以尽可能的缩小。

/post/flex-box-with-overflow/layout-min-width.png

或者给 flex item 设置一个具体的 width

不过此时由于 flex item 默认 flex-grow:0, 不会延展尺寸,因此还要设置 flex-grow:1, 让 flex item 去延展填满剩余空间。

/post/flex-box-with-overflow/layout-width.png

如果都不设置,那么 flex item 此时的最小尺寸由 content size suggestion 决定:

The content size suggestion is the min-content size in the main axis, clamped, if it has an aspect ratio, by any definite min and max cross size properties converted through the aspect ratio, and then further clamped by the max main size property if that is definite.

min-content size 最终会由 CSS2.1§10.3.5 确定最小尺寸的计算,具体来说是:

Calculation of the shrink-to-fit width is similar to calculating the width of a table cell using the automatic table layout algorithm. Roughly: calculate the preferred width by formatting the content without breaking lines other than where explicit line breaks occur, and also calculate the preferred minimum width, e.g., by trying all possible line breaks. CSS 2.1 does not define the exact algorithm. Thirdly, find the available width: in this case, this is the width of the containing block minus the used values of 'margin-left', 'border-left-width', 'padding-left', 'padding-right', 'border-right-width', 'margin-right', and the widths of any relevant scroll bars.

Then the shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width).

  • preferred width: 元素不换行的宽度,本例子就是文本的长度

  • preferred minimum width: 对元素尝试各种换行,得到的最小宽度(但由于 设置了 white-space:nowrap; , 所以应该是和 preferred width 一样的)

  • available width: 盒子宽度,400px

根据公式,最小宽度就是 min(max(2000+px, 400px), 2000+px) => 2000+px

所以默认最小宽度很长,导致了溢出。

/post/flex-box-with-overflow/layout-default.png

推论

使用 flex 布局的时候,可以给 flex item 设置 min-width: 0 ,从而保证 flex item 能够尽可能地收缩,避免溢出 flex container。

当碰到 flex item 的尺寸和预想中不一样时,可以考虑是不是这个原因导致的。