先记住这个答案
普通 div 上写 overflow: hidden、white-space: nowrap、text-overflow: ellipsis 就能出省略号,因为 div 的宽度由父容器决定,文字超出就溢出。表格不一样,默认的自动表格布局会先量每个单元格内容的宽度,再按内容分配列宽。文字不换行时,最小宽度就是整行文字的长度,列会被直接撑宽,单元格根本没有溢出,省略号也就无从谈起。解决办法是让列宽不再跟着内容走:给 table 加 table-layout: fixed 和一个明确宽度,或者在单元格里包一层有固定宽度的块来做省略。
- 省略号的前提是真的溢出,自动表格布局让单元格不溢出
- 固定表格布局还需确定表格宽度
- 单元格省略仍需单行和裁剪
- 不想改布局,就在单元格里包一层
div做省略
为什么同样的三行 CSS 在表格里失效
先看一个常见的失败写法,给 td 设了宽度和省略三件套,页面上还是一整行长文字把表格撑出了容器。
width 只是建议值HTML<table style="width: 300px">
<tr>
<!-- 设了 width,但自动布局下它只是个最小建议 -->
<td style="width: 100px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis">
这是一段很长很长很长很长很长的商品名称
</td>
<td>价格</td>
</tr>
</table>
<!-- 结果:第一列被撑到整段文字那么宽,表格超出 300px,没有省略号 -->table-layout 的默认值是 auto。这种模式下浏览器要先读完整张表的内容,算出每一列的最小宽度和理想宽度,再决定列宽。单元格上写的 width 在这里更像下限:内容放不下时,列宽会继续往上长。nowrap 又让文字不能折行,于是最小宽度等于整段文字宽度,列被撑开,表格总宽也跟着超过设定的 300px。
对比普通块:块级元素的宽度由包含块往下传,内容再长也改变不了它,多出来的部分就是溢出,text-overflow 才有东西可处理。表格是反过来的,宽度是从内容往上算的,这就是两者的本质差别。
最常用的修法:table-layout: fixed
把表格切到固定布局,列宽就不看内容了。浏览器只看 table 的宽度、<col> 的宽度和第一行单元格的宽度,剩下的平均分,后面行的内容再长也不会让列变宽。
- 固定布局加省略三件套
table上写table-layout: fixed; width: 100%,表头的th上给每列设宽度,然后单元格统一加overflow: hidden、white-space: nowrap、text-overflow: ellipsis。这时长文字超出列宽,就会正常显示省略号。 要注意table-layout: fixed必须配合table的明确宽度才生效,width还是auto时浏览器会退回自动布局。另外列宽是从第一行读的,所以宽度要写在表头或<col>上,写在第十行的某个单元格上不起作用。 固定布局还有个附带好处:浏览器读到第一行就能确定列宽,不用等整张表加载完,大表格渲染会更快。代价是列宽不再自适应内容,短内容的列也会占着固定宽度。
不想改布局时的两种办法
有时候表格要保留自适应列宽,只希望某一列出省略号,这时可以用下面两种办法。
- 单元格里包一层块
- 在
td里放一个div,把宽度和省略三件套写在这个div上,比如max-width: 200px。div是普通块,宽度由自己的max-width限住,文字超出就溢出,省略号就能出来。表格量内容时,量到的是这个div的宽度,所以列也不会被撑开。这是组件库里最常见的做法。 max-width: 0的技巧- 另一个流传很广的写法是给
td设max-width: 0,再配合百分比width。它让单元格对最小列宽的贡献变成 0,列宽改由剩余空间分配,在主流浏览器里能出省略号。不过 CSS 规范没有定义min-width、max-width作用在单元格上的效果,这个写法依赖的是浏览器的实现,项目里更推荐前两种。
容易答错的地方
- 以为给
td设width就够了 - 自动布局下单元格的
width挡不住内容,列照样会被撑宽。要么切固定布局,要么在里面包一层块来限宽。 - 只加
table-layout: fixed不给宽度 table的宽度是auto时,固定布局不会生效,表现和原来一样。至少要给table一个width,常见的是100%。- 漏掉
white-space: nowrap - 不加
nowrap,文字会折成多行,不会在水平方向溢出,也就没有省略号。想要多行省略,要用-webkit-line-clamp那套写法,和单行省略是两回事。
面试官还会怎么问?
省略掉的完整内容怎么让用户看到?
最简单是给单元格加 title 属性,悬停时显示原生提示。组件库一般会判断 scrollWidth > clientWidth,只有真的被截断时才显示自定义的 Tooltip。
table-layout: fixed 下没设宽度的列怎么分?
浏览器先扣掉设了宽度的列,剩下的宽度平均分给没设宽度的列。所以一般只给固定内容的列设宽度,比如操作列、日期列,名称列留空让它吃掉剩余空间。
用 div 加 display: table-cell 模拟的表格也有这个问题吗?
有。display: table 和 display: table-cell 走的是同一套表格布局算法,同样需要 table-layout: fixed 加宽度,或者在单元格里包一层块。现在做这类布局更常用 Grid,列宽用 minmax(0, 1fr) 就能让长文字正常溢出。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。