
一行里放两件东西:左边是可能很长的标题,右边是固定宽的按钮。标题太长了要截断,加省略号。样式写得很标准,三行都在:
.title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
结果右边那个按钮被推到看不见的地方,整行还能横向滚。把标题换成短句子,按钮立刻回到该在的位置。问题不在那三行,三行都没写错。
一样的三行 CSS,只换掉外层的结构
搭了一个最小页面来隔离这件事:外层是宽 320px 的 flex 行,右侧一个 flex: 0 0 64px 的固定宽兄弟元素,中间放标题。四种结构,量同一批数字。
浏览器:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36
页面宽度固定 320px 的 flex 行,右边一个 64px 的固定宽兄弟元素
取到测量结果:true
情形一和情形二用的是同一套 CSS 类,差别只在元素层级:情形一的标题元素自己就是 flex 项,情形二的标题外面多包了一层普通 div,中间那层才是 flex 项。前者正常出省略号,后者把行撑到溢出 116px,一个字都没截。
差的那 116 像素是从哪里来的
320px 的容器,扣掉 64px 的固定宽兄弟和边框内边距,留给标题的位置是 250px。标题那句的自然宽度量出来是 367.77px,多出 117.77px 顶到行外面,加上边框和 padding 正好对上溢出的 116px。
数字里最能说明问题的是情形二的这两个:中间层宽 367.77,文本元素 clientWidth 368,两个数一样大,说明文本元素拿到的完整宽度就是它的自然宽度,它从来没有被压缩过。没有压缩,就没有溢出可截,text-overflow: ellipsis 也就没有可显示省略号的机会。
情形三只做了一件事:给中间那一层加 min-width: 0。中间层宽度立刻落到 250px,文本元素 clientWidth 变成 250、scrollWidth 还是 368,省略号出现了。
原因是 flex 项的 min-width 默认值不是 0,是 auto,而 auto 在这里的含义是「不小于内容的最小宽度」。规范里管这个叫自动最小尺寸。情形一没这个问题,因为标题元素自己就是 flex 项,同时它带着 overflow: hidden,自动最小尺寸会自动归零,压得下来。多包一层之后,带 overflow: hidden 的变成了里面的文本元素,而待压缩的那一层是外面那张没写任何 overflow 的 div。
overflow 写错地方也能碰巧修好
情形四是另一条路:不给中间层写 min-width: 0,只把 overflow: hidden 补到它身上。宽度同样落到 250px,省略号同样出现,量出来的数字和情形三一模一样。
这条路能成立,逻辑和情形一相同:那一层的 overflow 不再是 visible,自动最小尺寸就归零了。问题是它的意图是错的,加 overflow: hidden 的念头通常是「那我把多余的裁掉」,而这一层本来不需要裁东西,该裁的活已经由里面的文本元素干了。哪天有人来清理,看到这一层既没有滚动也没有溢出,顺手把这行删掉,省略号就又没了。
min-width: 0 写的意图是「这一层可以被压到零宽」,说的正是要做的事。修法上我倾向于这一条,而不是顺手补一个 overflow: hidden 把副作用当方案用。
量三个数字就能分清是哪种坏
这类问题的排查不需要猜,取一个元素量三个数就够了:外层的 getBoundingClientRect().width、文本元素的 clientWidth、文本元素的 scrollWidth。
clientWidth等于scrollWidth:元素没被限制,宽度就是内容的自然宽度,问题在压缩没生效。clientWidth明显小于scrollWidth:压缩生效了,问题在别处,可能是text-overflow没写,或者元素不是块级(行内元素上这两行样式都不起作用)。- 外层还多量一个
scrollWidth:大于clientWidth就说明它自己在横向溢出,被撑破的是这一层。
== 结论行 ==
情形一 文本元素即 flex 项 溢出 0px 省略号 true
情形二 没写 min-width 的中间层 溢出 116px 省略号 false
情形三 中间层 min-width:0 溢出 0px 省略号 true
情形四 中间层只加 overflow:hidden 溢出 0px 省略号 true
上面四行是同一份页面的最终读数,true 表示省略号确实出现了。这一组数字比肉眼看截图可靠:截图上「省略号没出现」和「省略号出现了但只有两个点」很难分清,宽度差多少是确定的。
真机上只在 Chromium 里量过,Safari 和 Firefox 没跑。这套最小尺寸的算法是通行行为,但换浏览器第一次上线时,还是照着那三个数字在自己的页面上量一遍更保险。
顺手会碰到的两种同类结构
表格单元格里的单行省略是同一个坑的另一副面孔。table-layout: auto 下单元格宽度由内容决定,给单元格里的文本加省略号同样无效,得先把 table-layout: fixed 打开,列宽有确定来源之后截断才有意义。
从 flex 换成 grid 之后写法变了,坑没变:grid-template-columns: 1fr 64px 里的 1fr 最小也是 auto,要写 minmax(0, 1fr) 才允许压缩到零。左右分栏的列表从 flex 改到 grid 时,这个写法最容易漏,症状和前面那 116px 的溢出一样。