[前端]弹性盒子 align-items 与 align-content 的区别

[前端]弹性盒子 align-items 与 align-content 的区别

涉及定义的地方,笔者已尽力还原文档的意思。如果读者依旧感到阅读困难,建议直接查看英文文档,链接已在文中给出。

如题,flexbox 中的这两个属性在概念上很接近。笔者每次使用时,都有种看韩国女明星的错觉。感觉哪里有点不一样,但又说不上来。

先来看下官方定义。

定义

  • align-items:
    • 作用对象:弹性盒子容器(flex containers);
    • 描述:该属性可以控制弹性容器中成员在当前行内的对齐方式。当成员设置了align-self 属性时,父容器的 align-items 值则不再对它生效;
    • w3c标准原文:链接
  • align-content:
    • 作用对象:多行弹性盒子容器(multi-line flex containers);
    • 描述:当弹性容器在正交轴方向还存在空白时,该属性可以控制其中所有行的对齐方式。Note:该属性无法作用于单行 (flex-wrap: no-wrap) 弹性盒子;
    • w3c标准原文:链接

对比

相同点:

  • 都被用来设置对齐行为。

不同点:

  • align-items 的设置对象是行内成员;
  • align-content 的设置对象是所有行,且只有在多行弹性盒子容器中才生效。

举栗

以 center 关键字为例。

文档对两个属性 cetner 关键字的描述:

  • align-items:行内成员会在其边界盒正交轴上被居中(如果行正交尺寸小于行内成员尺寸,行内成员将会在正交轴两方向等量溢出)。链接
  • align-content:所有行被集中(挤)在弹性容器(正交轴)中间。它们彼此之间齐平,并且跟弹性盒子正交起始边界的空白与跟弹性盒子正交结束边界的空白相等。(如果溢出空白为负数,所有行将会在正交轴两方向等量溢出)链接

接下来我们先根据定义预测对齐的行为,再运行代码验证看是否一致。戳这里自己动手验证。

列出所有会产生影响的条件,包括:是否自动换行 (flex-wrap: wrap/no-wrap),一行和多行,留和不留多余空白。3个条件排列组合后有8中可能。

1. 自动换行:no-wrap,行数:1行,留白:不留。

HTML:

<div>
  <span>1</span>
  <span>1</span>
  <span>1</span>
</div>

CSS:

div{
  display: flex;
  background-color: DodgerBlue;
  /* align-items or align-content: center; */
}

span{
  margin: auto;
  background-color: #f1f1f1;
  width: 280px;
  margin: 10px;
  text-align: center;
  line-height: 75px;
  font-size: 30px;
}

预测:

  • align-items:无效果。因为容器高度会自适应;
  • align-content:无效果。因为单行模式下该属性不起作用(见 align-content 定义)。

验证:

  • align-items:✅
1-1
  • align-content:✅
1-2

2. 自动换行:no-wrap,行数:1行,留白:留。

HTML:

<div>
  <span>1</span>
  <span>1</span>
  <span>1</span>
</div>

CSS:

div{
  display: flex;
  background-color: DodgerBlue;
  height: 300px; /* 设置高度产生留白 */
  /* align-items or align-content: center; */
}

span{
  margin: auto;
  background-color: #f1f1f1;
  width: 280px;
  margin: 10px;
  text-align: center;
  line-height: 75px; /* 设置高度产生留白 */
  font-size: 30px;
}

预测:

  • align-items:正交轴居中。因为是单行模式,所以容器高度即整行高度;
  • align-content:无效果。因为单行模式下该属性不起作用(见 align-content 定义)。

验证:

  • align-items:✅
2-1
  • align-content:❌
2-2

分析错误:

事实上 align-content 确实没有产生影响,是容器的 align-items 和成员的 hight 对布局造成了影响

2-3 预测的2-2

弹性盒子容器没有设置 align-items 属性,成员也没有设置 align-self 属性,所以盒子和成员的对齐行为都是默认值 (initial) 。align-self 的默认值是 auto 关键字,其释义是“使成员的对齐行为与容器的 align-items 的值一致”。再看 align-items 的默认值是 stretch[1] 关键字,其释义是“如果成员的长宽是 auto ,而且容器内正交轴方向的两个边距都是 auto,那么成员将会被拉伸。成员将会尽可能的在正交方向上填满所在行。”。

问题已经很明朗了。align-items 是默认值 stretch,成员的 height 是默认值 auto,已经满足了拉伸的条件,所以产生了图 2-2 的效果。

要想得到图 2-3 的效果,只要破坏其中任何一个条件即可。这里给出几种方法:

  • 设置成员 height 为不可被拉伸的值;
  • 设置成员 align-self 为 flex-start;
  • 设置容器 align-items 为 flex-start。

3. 自动换行:no-wrap,行数:>1行,留白:不留。(不可能发生,不做讨论)

4. 自动换行:no-wrap,行数:>1行,留白:留。(不可能发生,不做讨论)

5. 自动换行:wrap,行数:1行,留白:不留。(没有讨论价值,留给读者自行验证)

6. 自动换行:wrap,行数:1行,留白:留。(是例 8 的一个特例,不再单独讨论)

7. 自动换行:wrap,行数:>1行,留白:不留。(没有讨论价值,留给读者自行验证)

8. 自动换行:wrap,行数:>1行,留白:留。

HTML:

<div>
  <span>1</span>
  <span>2</span>
  <span>3</span>
  <span>4</span>
  <span>5</span>
  <span>6</span>
  <span>7</span>
</div>

CSS:

div{
  display: flex;
  background-color: DodgerBlue;
  height: 450px; /* 设置高度产生留白 */
  width: 900px; /* 设置宽度产生换行 */
  flex-wrap: wrap; /* 设置自动换行 */
  /* align-items or align-content: center; */
}

span{
  margin: auto;
  background-color: #f1f1f1;
  width: 280px;
  margin: 10px;
  text-align: center;
  line-height: 75px;
  font-size: 30px;
}

预测:

  • align-items:整个容器在正交轴方向上被均分为三行,成员在所在行内正交居中;
  • align-content:受默认值影响,整个容器在正交轴方向上被均分为三行,成员伸展填满所在行。

验证:

  • align-items:✅
4-1
  • align-content:❌
4-2

错误分析:

成员之所以没有拉伸,是因为成员在正交方向的两个边距都为 0。

4-3 预测的4-2

笔者之所以会做出 4-3 图中的预测,是因为 align-items 的默认值是 stretch。为什么 stretch 没有生效?再回到上文查看 stretch 释义,成员发生(能且需要)拉伸需要满足两个条件。这里满足了①长宽为 auto ,但没有满足②正交方向的两个边距为 auto 。事实上成员正交方向的两个边距为 0,而造成这种情况的原因正是 align-content 为 cetner。

align-content 的 cetner 关键字释义中提到 “所有行被集中(挤)在弹性容器(正交轴)中间(Lines are packed toward the center of the flex container)”。这里“挤”字是笔者在领悟到文档中 be packed toward 的意味后加上的。显然被 align-content 对齐到中间的行们彼此之间已经没有了多余的空间 (extra space),成员们的正交方向边距都是 0,所以无法拉伸了。

让我们验证一下这个想法。仅把第一个成员 “1” 的高度改为 100px,理论上第一行其他几个成员就多出了额外的 25px 空间,为了填满空白它们会被拉伸。

4-4

确实如此,再次感叹下文档的用词真的非常准确。

总结

回到定义,align-items 和 align-content 的差异可以总结为如下两点。

  1. align-items 的上下文是行内,align-content 的上下文是弹性盒子容器;
  2. align-items 控制成员的对齐行为,align-content 控制所有行的对齐行为。

最后祭上w3c标准里的两张导图。

illustration of align-items
illustration of align-content

参考

  1. ^MDN 所注默认值有误,以 w3c 文档为准
编辑于 2020-04-01 13:43