弹性盒子的典型用例

在本指南中,我们将探讨一些 Flexbox 的常见用例——在这些情况下,Flexbox 比其他布局方法更具优势。

为何选择 Flexbox?

当你想要在一维空间中布局一组项目或控制项目之间的间距时,Flexbox 通常是正确的 CSS 布局解决方案。在本指南中,我们将探讨 Flexbox 的一些典型用例。

导航的一个常见模式是将项目列表显示为水平条。这可能是最常见的 Flexbox 示例,可以被认为是理想的 Flexbox 用例。

当我们有一组想要水平显示的项目时,我们很可能会遇到额外的空间。我们需要决定如何处理这些空间,有几个选项。我们可以将空间显示在项目之外——因此在项目之间或周围用空白间隔开——或者我们将额外的空间吸收到项目内部,因此需要一种方法让项目增长并占据这些空间。

空间分布在项目之外

为了在项目之间或周围分布空间,我们使用 Flexbox 中的对齐属性和 justify-content 属性。你可以在 在弹性容器中对齐项目 中阅读更多关于此属性的信息,它处理主轴上的项目对齐。

在此示例中,我们以其自然大小显示项目,并使用 justify-content: space-between 来均匀分布项目。你可以使用 space-aroundspace-evenly 值来改变空间的分布方式。你也可以使用 start 将空间放在项目末尾,使用 end 将其放在项目之前,或使用 center 来居中导航项目。

html
<nav>
  <ul>
    <li><a href="#">Page 1</a></li>
    <li><a href="#">Page 2</a></li>
    <li><a href="#">Page 3 is longer</a></li>
    <li><a href="#">Page 4</a></li>
  </ul>
</nav>
css
nav {
  border: 2px solid #eeeeee;
}

nav a {
  text-decoration: none;
  color: black;
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
  padding: 10px;
  display: block;
}

nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: space-between;
}

空间分布在项目内部

导航的另一种模式是将可用空间分布在项目本身内部,而不是在它们之间创建空间。flex 属性允许项目相互按比例增长和缩小,如 控制弹性项目沿主轴的比例 中所述。

如果你想尊重导航项目的尺寸属性,但让可用空间在它们之间平均分配,你可能会使用 flex: auto,它是 flex: 1 1 auto 的简写形式——所有项目都从 auto 的 flex-basis 增长和缩小。这意味着较长的项目将拥有更多的空间,因为它从一个更大的尺寸开始,即使分配给它的可用空间与其他的相同。

在下面的实时示例中,尝试将 flex: auto 更改为 flex: 1。这个 flex: 1 1 0 的简写形式会导致所有项目变为相同的宽度,因为它们以 0flex-basis 为基础工作,从而使所有空间均匀分布。

html
<nav>
  <ul>
    <li><a href="#">Page 1</a></li>
    <li><a href="#">Page 2</a></li>
    <li><a href="#">Page 3 is longer</a></li>
    <li><a href="#">Page 4</a></li>
  </ul>
</nav>
css
nav {
  border: 2px solid #eeeeee;
}
nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
}

nav a {
  text-decoration: none;
  color: black;
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
  padding: 10px;
  display: block;
}

nav li {
  flex: auto;
}

分离式导航

另一种在主轴上对齐项目的方法是使用自动外边距。这使得导航栏的设计模式得以实现,其中一组项目左对齐,另一组项目右对齐。这里我们使用 使用自动外边距进行主轴对齐 中描述的自动外边距技术。

项目在主轴上以 normal 对齐,这相当于 start,因为这是 Flexbox 的初始行为。gap 属性在项目之间创建间隙。我们将最后一个项目通过设置其 margin-left 值为 auto 来右对齐。你可以将类从一个项目移动到另一个项目,以改变分割发生的位置。

html
<nav>
  <ul>
    <li><a href="#">Page 1</a></li>
    <li><a href="#">Page 2</a></li>
    <li><a href="#">Page 3 is longer</a></li>
    <li class="push-right"><a href="#">Page 4</a></li>
  </ul>
</nav>
css
nav {
  border: 2px solid #eeeeee;
}

nav a {
  text-decoration: none;
  color: black;
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
  padding: 10px;
  display: block;
}

nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  gap: 20px;
}

.push-right {
  margin-left: auto;
}

居中项目

开发人员中长期流传的一个笑话是,网页设计中最困难的问题是垂直居中。使用 Flexbox 对齐属性,垂直居中内容非常简单,如下面的实时示例所示。

点击 “播放” 并尝试改变对齐方式,例如使用 start 将项目对齐到开头,或使用 end 对齐到末尾。

html
<div class="box">
  <div></div>
</div>
css
.box {
  height: 300px;
  border: 2px dotted rgb(96 139 168);
  display: flex;
  align-items: center;
  justify-content: center;
}

.box div {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
  width: 100px;
  height: 100px;
}

借助 CSS 盒模型对齐 属性,你可以在没有 Flexbox 的情况下垂直居中一个元素。在上面的示例中,尝试从盒子中移除 Flex 属性,然后添加 align-content: center。然后,为你想要水平居中的元素添加 margin: auto

无论你是使用 flexbox 还是 grid 来布局卡片组件列表,这些布局方法都只作用于 flex 或 grid 组件的直接子元素。这意味着,如果你的内容量可变,卡片将拉伸到网格区域或 flex 容器的高度。内部的任何内容都使用常规的块级布局,这意味着内容较少的卡片中,页脚会上升到内容底部,而不是粘在卡片底部。

Two card components showing that the internals of the component do not stretch with the wrapper.

Flexbox 解决了这个问题。我们将卡片设置为一个弹性容器,使用 flex-direction: column。然后我们将内容区域设置为 flex: 1,这是 flex: 1 1 0 的简写形式——项目可以从 0 的弹性基准增长和缩小。由于这是唯一可以增长的项目,它会占据弹性容器中所有可用空间,并将页脚推到底部。如果你从实时示例中移除 flex 属性,你将看到页脚会直接移动到内容下方。

html
<div class="cards">
  <div class="card">
    <div class="content">
      <p>This card doesn't have much content.</p>
    </div>
    <footer>Card footer</footer>
  </div>
  <div class="card">
    <div class="content">
      <p>
        This card has a lot more content which means that it defines the height
        of the container the cards are in. I've laid the cards out using grid
        layout, so the cards themselves will stretch to the same height.
      </p>
    </div>
    <footer>Card footer</footer>
  </div>
</div>
css
body {
  font-family: sans-serif;
}
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  grid-gap: 10px;
}

.card {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  display: flex;
  flex-direction: column;
}

.card .content {
  padding: 10px;
  flex: 1 1 auto;
}

.card footer {
  background-color: rgb(96 139 168 / 0.2);
  padding: 10px;
}

媒体对象

媒体对象——一个图像或其他媒体元素与一些描述性文本并排——是网页设计中常见的模式。媒体对象应该能够翻转——将图像从一侧移动到另一侧。

这种模式用于评论和图像与其描述并置的其他地方。我们可以使用 flexbox 允许媒体对象中包含图像的部分从图像中获取其尺寸信息,而媒体对象的内容则灵活地占据剩余空间。

在此示例中,媒体对象对齐到 flex-start,并且 .content 设置为增长,增长因子设置为 1。这些属性与我们上面用于列布局卡片模式的属性相同。

html
<div class="media">
  <div class="image">
    <img
      alt="A colorful balloon against a blue sky"
      src="https://mdn.github.io/shared-assets/images/examples/balloon.jpg" />
  </div>
  <div class="content">
    This is the content of my media object. Items directly inside the flex
    container will be aligned to flex-start.
  </div>
</div>
css
img {
  max-width: 100%;
  display: block;
}

.media {
  border: 2px dotted rgb(96 139 168);
  display: flex;
  align-items: flex-start;
}

.media .content {
  flex: 1;
  padding: 10px;
}

在这个实时示例中,你可能想尝试一些与你在设计中限制媒体对象的不同方式相关的事情。

为防止图像变得太大,你应该为图像添加一个 max-width。由于媒体对象的这一侧使用 Flexbox 的初始值,它可以缩小,但不能增长,并且使用 autoflex-basis。应用于图像的任何 widthmax-width 都将成为 flex-basis

css
.image img {
  max-width: 100px;
}

你也可以让两边按比例增长和缩小。如果你将两边都设置为 flex: 1,它们将从 0flex-basis 增长和缩小,因此你最终会得到两个大小相等的列。你可以以内容为指导,将两者都设置为 flex: auto,在这种情况下,它们将从内容的大小或直接应用于弹性项目的任何大小(例如图像上的 width)增长和缩小。

css
.media .content {
  flex: 1;
  padding: 10px;
}

.image {
  flex: 1;
}

你也可以为每一侧设置不同的 flex-grow 因子,例如将带图像的一侧设置为 flex: 1,内容侧设置为 flex: 3。这意味着它们将使用 0flex-basis,但根据你分配的 flex-grow 因子以不同的速率分配该空间。我们用于此的 flex 属性在指南 控制弹性项目沿主轴的比例 中有详细描述。

css
.media .content {
  flex: 3;
  padding: 10px;
}

.image {
  flex: 1;
}

翻转媒体对象

要切换媒体对象的显示,使图像在右侧,内容在左侧,我们将 flex-direction 属性设置为 row-reverse

在此示例中,我们在 media 类旁边添加了一个 flipped 类。从 HTML 中移除该类,查看显示如何变化。

html
<div class="media flipped">
  <div class="image">
    <img
      alt="A colorful balloon against a blue sky"
      src="https://mdn.github.io/shared-assets/images/examples/balloon.jpg" />
  </div>
  <div class="content">
    This is the content of my media object. Items directly inside the flex
    container will be aligned to flex-start.
  </div>
</div>
css
img {
  max-width: 100%;
  display: block;
}

.media {
  border: 2px dotted rgb(96 139 168);
  display: flex;
  align-items: flex-start;
}

.flipped {
  flex-direction: row-reverse;
}

.media .content {
  flex: 1;
  padding: 10px;
}

表单控件

Flexbox 在样式化表单控件时特别有用。表单有几个小元素,我们通常希望它们彼此对齐。一个常见的模式是 <label><input> 对与一个 <button> 结合使用,可能用于搜索表单或新闻订阅表单,你希望访问者输入他们的电子邮件地址。

Flexbox 使这种类型的布局易于实现。<label><input><button> 包含在一个设置为 display: flex 的包装器中。flex 属性允许 <input> 字段增长,而按钮和标签不增长。文本输入字段将根据可用空间增长和缩小。

html
<form class="example">
  <div class="wrapper">
    <label for="text">Label</label>
    <input id="text" type="text" />
    <input type="submit" value="Send" />
  </div>
</form>
css
* {
  font: 1.1em sans-serif;
}

.wrapper {
  display: flex;
  border: 1px solid rgb(96 139 168);
}
.wrapper > * {
  padding: 10px;
  border: none;
  color: white;
}
.wrapper > input[type="text"] {
  background-color: rgb(96 139 168 / 0.5);
  border-right: 1px solid rgb(96 139 168);
  flex: 1 1 auto;
}
.wrapper input[type="submit"] {
  background-color: rgb(96 139 168);
  color: white;
}
.wrapper label {
  background-color: #666666;
}

这种模式可以让你更容易地为你的设计创建一个表单元素库,轻松地适应额外元素的添加。你正在利用 Flexbox 的灵活性,将不增长的项目与会增长的项目混合使用。

总结

在探索上述模式时,你可能已经开始了解如何思考使用 flexbox 实现所需结果的最佳方法。通常你有不止一种选择。将不能伸展的项目与可以伸展的项目混合,利用内容来决定大小,或者让 flexbox 按比例分配空间。这取决于你。

思考如何最好地呈现你拥有的内容,然后看看 Flexbox 或其他布局方法如何帮助你实现它。