先记住这个答案
省略号能出现的前提是盒子比文字窄。Flex 子项的 min-width 默认值是 auto,它会算出内容的最小宽度,文字加了 white-space: nowrap 之后,这个最小宽度就是整行文字的长度,子项宁可把容器撑破也不收缩。解决办法是给这个 Flex 子项加 min-width: 0,让它允许缩到比内容窄;也可以给它加 overflow: hidden,因为 overflow 不是 visible 的子项,自动最小宽度会变成 0。
- Flex 项自动最小宽度可能卡住收缩
- 省略需单行、裁剪与省略标记
- 给文本 Flex 项设
min-width: 0 - 嵌套 Flex 时,从容器到文本每一层子项都要能收缩
列表项里的标题撑破了一整行
最常见的场景是左边头像、中间标题、右边时间的一行布局。中间标题太长时,希望它省略,结果时间被挤出了容器。
<div class="row">
<img class="avatar" src="a.png" />
<div class="main">
<p class="title">一个非常非常长的标题,长到超过这一行能放下的宽度</p>
</div>
<span class="time">10:30</span>
</div>
<style>
.row { display: flex; width: 320px; }
.main { flex: 1; } /* 问题在这:min-width 默认 auto */
.title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis; /* 写对了,但 .main 没变窄,不生效 */
}
/* 修法:.main { flex: 1; min-width: 0; } */
</style>.title 的三件套一个没少,但它是块元素,宽度跟着父级 .main 走。.main 是 Flex 子项,min-width: auto 让它的最小宽度等于里面那行不换行的标题长度,flex: 1 里的 flex-shrink: 1 想收缩也收不动。于是 .main 按整行文字的宽度排,.time 被挤到容器外面。
给 .main 加上 min-width: 0,它就可以缩到剩余空间那么宽,.title 跟着变窄,省略号出现。
为什么 Flex 要设计成 min-width: auto
普通块元素的 min-width 初始值也叫 auto,但对它们来说就等于 0。Flex 子项例外,规范专门给它定义了「自动最小尺寸」:取内容的最小宽度(最长的那个不可断开的片段)。
这样设计是为了防止内容被压坏。比如一行 Flex 里放了一张图片和一个长 URL,如果子项能缩到 0,图片会被压扁、URL 会叠到别人身上。默认不让它缩到比内容更窄,大多数布局看起来是对的。代价就是做省略号、做可滚动区域时,要手动把这个保护关掉。
- 两种修法的区别
min-width: 0只是把最小宽度放开,不改变溢出行为,子项里其它内容溢出时照样可见。overflow: hidden会顺带裁掉子项里溢出的一切,比如下拉菜单、tooltip 这种需要飘出去的东西也会被剪掉。所以在外层容器上优先用min-width: 0,在文本元素本身上用三件套里的overflow: hidden就够了。
多层嵌套时加在哪一层
真实项目里文本往往包了好几层,每一层都是 Flex 子项。规则很简单:从外层 Flex 容器到文本元素之间,每一个作为 Flex 子项的祖先都要能收缩。
排查时从文本元素往上找,看哪一层的宽度比容器宽。在开发者工具里选中那一层,计算样式里 min-width 显示 auto、而实际宽度等于文字长度,就是它。竖向布局也一样,纵向的 Flex 子项默认 min-height: auto,想让中间区域滚动,要给它加 min-height: 0。
容易答错的地方
- 以为加
flex-shrink: 1就能收缩 flex-shrink默认就是 1。子项能缩多少,下限是min-width,min-width: auto把下限定在内容宽度,flex-shrink再大也缩不过这条线。- 以为给文本元素设了
width: 100%就行 width: 100%是相对父级的宽度。父级 Flex 子项本身已经被内容撑宽了,100% 也跟着宽,问题出在父级而不是文本自己。- 把
min-width: 0加在 Flex 容器上 - 要加在 Flex 子项上,也就是
display: flex那个元素的直接孩子。加在容器上没有作用,除非这个容器本身又是外层 Flex 的子项。
面试官还会怎么问?
Grid 布局里会不会有同样的问题?
会。Grid 项目的最小尺寸默认也是 auto,而且 1fr 的轨道最小值也是 auto。长文本会把 1fr 的列撑宽。修法是写成 minmax(0, 1fr),或者给 Grid 项目加 min-width: 0。
多行文本省略在 Flex 里怎么处理?
多行省略用的是 display: -webkit-box 配 -webkit-line-clamp 和 overflow: hidden。它只限制行数,宽度问题还是一样:如果外层 Flex 子项被长单词撑宽,也要加 min-width: 0。
除了省略号,min-width: auto 还会导致什么问题?
最常见的是中间区域滚动失效。比如一个上中下的纵向 Flex 页面,中间区域是一个 flex: 1 的包裹层,真正写 overflow: auto 的列表在它里面。包裹层的 min-height 是 auto,被列表撑高,整页跟着变长,列表自己反而不出滚动条。给包裹层加 min-height: 0 就能解决。如果写 overflow: auto 的元素本身就是 Flex 子项,它的自动最小高度已经是 0,不会有这个问题。另外代码块、表格放进 Flex 子项里横向撑破页面,也是同一个原因。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。