背景
面对需要文本溢出截断的场景,通常我们为容器设置固定宽度,再设置文本溢出省略号截断来达成目的
{
width: 300px;
overflow: hidden;
white-space: nowrap; // 不换行
text-overflow: ellipsis; // 文本溢出省略号截断
}
考虑一种特殊场景,flex容器中包含一个默认填充整体区域的元素,该子元素自身为flex容器,包含左右两部分,左侧内容区默认填充剩余区域,右侧内容区宽度固定。按照上述方式,渲染效果如下,不符合预期。
从上述情况来看,导致文本溢出没有发生截断的原因是flex布局下元素宽度随内容增加了,所以没有发生溢出。为解决上述问题,根源还是要搞清楚flex布局下元素大小的计算规则,具体可参照下文 flex布局章节。
flex布局
flex布局是一种空间分布和对齐的布局方式,通常被称为flexbox。采用了flexbox的区域叫做flex容器。包含主轴、交叉轴两根轴线。
flex容器属性
flex-direction
表示主轴元素排列方向
row | column | row-reverse | column-reverse,默认为row
flex-wrap
表示是否允许换行
nowrap | wrap | wrap-reverse,默认为nowrap
flex元素属性
flex-grow
表示当前元素沿主轴方向相对于flex容器中其他flex子元素的增长程度。
-
正值:增长;例如元素A flex-grow: 1,B flex-grow: 2,容器剩余空间会被划分为
1 + 2 = 3份,分给A 1份,B两份; -
0,默认值。不增长,使用元素的实际宽度。
-
负值:无效;
其实是flex容器放置完flex子元素后还有
可用空间,flex子元素各自按比例瓜分可用空间,瓜分比例计算公式
// 可用空间
const left_size = width_container - width_A + width_B;
// 各自所占比例
const total = grow_A + grow_B;
const proportion_A = grow_A / total;
const proportion_B = grow_B / total;
// 各自可以增加的大小 = 各自所占比例 * 可用空间
const grow_size_A = proportion_A * left_size;
const grow_size_B = proportion_B * left_size;
flex-shrink
表示当前元素沿主轴方向相对于flex容器中其他flex子元素的收缩程度。收缩基准是flex-basic。
-
正值:收缩;
-
0,默认值。不收缩,使用元素的实际宽度。
-
负值:无效;
其实就是为了flex容器能够放下所有子元素,flex子元素各自让出一部分空间,关于让出的这一部分具体多大,可根据此公式计算:A、B、C三个元素,flex容器;
// 超出的尺寸
const oversize = width_A + width_B + width_C - width_container;
// 各自所占比例
const total = width_A * shrink_A + width_B * shrink_B + width_C * shrink_C;
const proportion_A = width_A * shrink_A / total;
const proportion_B = width_B * shrink_B / total;
const proportion_C = width_C * shrink_C / total;
// 各自应该收缩的大小 = 各自所占比例 * 超出的尺寸
const shrink_size_A = proportion_A * oversize;
const shrink_size_B = proportion_B * oversize;
const shrink_size_C = proportion_C * oversize;
flex-basis
表示元素所占空间大小。flex-basis在空间分配前初始化flex子元素的尺寸。补充一个概念:可用空间指的是flex 容器里除了元素所占的空间以外的富余空间。此处附上MDN中对可用空间的介绍。
- 指定宽度:auto | 百分数 |
<length>;若为0,flex 子元素的大小不在空间分配计算的考虑之内; content:基于flex元素的内容自动调整大小。- 新属性值,较老的浏览器版本不支持,具体参照Report problems with this compatibility data on GitHub
常见的尺寸关键词包括fill | max-content | min-content | fit-content。
方案
首先分析下各元素情况
- 灰边框元素固定宽度,
display: flex - 红边框元素
flex: 1,但没有指定宽度。flex:1是flex-grow: 1; flex-shrink: 1; flex-basis: 0;的缩写。flex-basis: 0:表明红边框元素的大小不在空间分配计算的考虑之内;flex-grow: 1:表示当前元素沿主轴方向增长尺寸,在父元素有可用空间时的占比为1。此处红边框元素没有兄弟元素,1 / 1 + 0 = 100%,所以自动填满父元素flex-shrink: 1:表示当前元素在主轴方向收缩尺寸,在父元素空间不足以排列flex元素时,与兄弟元素按照占比1 * 自身宽度 / (1 * 自身宽度 + 兄弟节点的shrink * 兄弟节点的宽度)缩小。由于flex-basis: 0,元素不会缩小;
- 红边框子绿色子元素
flex: auto,是flex-grow: 1; flex-shrink: 1; flex-basis: auto;的缩写。红边框粉色子元素flex: 0,是flex-grow: 0; flex-shrink: 1; flex-basis: 0%;的缩写flex-basis: auto:表明未进行空间分配前,占用flex容器的初始大小是元素自身大小;flex-grow:绿色子元素在父元素有可用空间时的占比为1,粉色子元素占比0。所以绿色子元素会占满父元素可用空间flex-shrink:绿色子元素和粉色子元素均为1,在父元素空间不足以排列两者时,与兄弟元素按照占比1 * 自身初始宽度 / (1 * 自身初始宽度 + 兄弟节点的shrink * 兄弟节点初始宽度)即自身初始宽度占比缩小。由于粉色子元素的flex-basis: 0,空间分配前认为粉色子元素没有占用空间,绿色子元素占比100%,将绿色子元素尺寸缩小至flex容器可容纳的大小;
根据上述分析,
- 红色边框元素不会缩小,当小于父flex容器时会自动填充;由于没有设置宽度,会由子元素撑起宽度;
- 绿色子元素在宽度较宽时,由于父元素(红色边框元素)没有宽度,不存在超出,仅会将父元素撑起来;而设置的文本超出截断也只是在超过父容器宽度时才会生效,所以最终呈现的效果是文本并没有截断,而是将红色边框元素撑出了它的父元素。
针对上述问题,解决方法非常明了,就是给红色边框元素一个宽度。考虑到需求中要求绿色子元素占据flex容器的剩余空间,可以将flex-basis: 固定占比/宽度值排除,为绿色子元素指定一个小于flex容器剩余空间的width即可。
本文设置了width: 0。
其他
css换行、不换行、溢出处理方案
- 自动换行
p { word-wrap:break-word; }
- 强制英文单词断行
p { word-break:break-all; }
- 强制不换行
p { white-space: nowrap; }
- 省略号
p { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; }