flex布局下文本溢出截断

1,354 阅读6分钟

背景

面对需要文本溢出截断的场景,通常我们为容器设置固定宽度,再设置文本溢出省略号截断来达成目的

{
    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中对可用空间的介绍。 image.png

常见的尺寸关键词包括fill | max-content | min-content | fit-content

方案

首先分析下各元素情况

  • 灰边框元素固定宽度,display: flex
  • 红边框元素flex: 1,但没有指定宽度。flex:1flex-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换行、不换行、溢出处理方案

  1. 自动换行
p { word-wrap:break-word; }
  1. 强制英文单词断行
p { word-break:break-all; }
  1. 强制不换行
p { white-space: nowrap; }
  1. 省略号
p { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; }

参考文档