布局

CSS 播客 - 009:布局

假设您是一名开发者,一位设计师同事交给您一个全新网站的设计稿。设计稿具有各种有趣的布局和组合:既有考虑视口宽度和高度的二维布局,也有需要流畅和灵活的布局。您如何决定使用 CSS 设置这些布局样式的最佳方式?

CSS 为我们提供了多种方法来解决布局问题,无论是在水平轴、垂直轴还是两者上。为上下文选择正确的布局方法可能很困难,而且通常您可能需要多种布局方法来解决您的问题。为了帮助您解决这个问题,在接下来的模块中,您将了解每种 CSS 布局机制的独特功能,从而为您的决策提供依据。

布局:简史

在 Web 的早期,比简单文档更复杂的设计都使用 <table> 元素进行布局。当 CSS 在 90 年代后期被浏览器广泛采用时,将 HTML 与视觉样式分离变得更加容易。CSS 为开发者打开了大门,使他们能够在不接触 HTML 的情况下完全改变网站的外观和风格。这种新功能激发了诸如 CSS Zen Garden 之类的项目,该项目旨在展示 CSS 的强大功能,以鼓励更多开发者学习它。

随着我们对 Web 设计和浏览器技术的需求不断发展,CSS 也在不断发展。您可以在 Rachel Andrew 的这篇文章中了解 CSS 布局以及我们处理布局的方法是如何随着时间推移而演变的。

A timeline showing how CSS has evolved over the years, starting in 1996 up to 2021

布局:现在和未来

现代 CSS 具有异常强大的布局工具。我们有专门的布局系统,我们将对我们可用的工具进行高级概览,然后再在接下来的模块中深入探讨 Flexbox 和 Grid 的更多细节。

了解 display 属性

display 属性执行两项操作。它执行的第一项操作是确定应用它的框是充当内联元素还是块级元素。

.my-element {
  display: inline;
}

内联元素的行为类似于句子中的单词。它们沿内联方向彼此相邻排列。诸如 <span><strong> 之类的元素通常用于设置包含元素(如 <p>(段落))中文本片段的样式,默认情况下是内联元素。它们还会保留周围的空格。

A diagram showing all the different sizes of a box and where each sizing section starts and ends

您无法在内联元素上设置显式的宽度和高度。任何块级边距和内边距都将被周围的元素忽略。

.my-element {
    display: block;
}

块级元素不会彼此相邻排列。它们会为自己创建新行。除非被其他 CSS 代码更改,否则块级元素将扩展到内联尺寸的大小,因此在水平书写模式下会跨越整个宽度。块级元素所有侧面的边距都将受到尊重。

.my-element {
    display: flex;
}

display 属性还决定了元素的子元素应该如何表现。例如,将 display 属性设置为 display: flex 会使该框成为块级框,并且还会将其子元素转换为 flex 项目。这启用了控制对齐、排序和流的 flex 属性。

Flexbox 和 Grid

有两种主要的布局机制可以为多个元素创建布局规则:flexboxgrid。它们具有相似之处,但旨在解决不同的布局问题。

Flexbox

.my-element {
    display: flex;
}

Flexbox 是一种用于一维布局的布局机制。沿单个轴(水平或垂直方向)布局。默认情况下,flexbox 会将其元素的子元素彼此相邻对齐(沿内联方向),并在块方向上拉伸它们,使它们都具有相同的高度。

项目将保持在同一轴上,并且在空间不足时不会换行。相反,它们会尝试挤压到彼此的同一行上。可以使用 align-itemsjustify-contentflex-wrap 属性更改此行为。

Flexbox 还会将子元素转换为 flex 项目,这意味着您可以编写规则来规定它们在 flex 容器内的行为方式。您可以更改单个项目的对齐、顺序和对齐方式。您还可以使用 flex 属性更改其收缩或增长方式。

.my-element div {
    flex: 1 0 auto;
}

flex 属性是 flex-growflex-shrinkflex-basis 的简写。您可以像这样展开上面的示例

.my-element div {
 flex-grow: 1;
 flex-shrink: 0;
 flex-basis: auto;
}

开发者提供这些底层规则,以提示浏览器在布局受到内容和视口尺寸挑战时应如何表现。这使其成为自适应 Web 设计的非常有用的机制。

Grid

.my-element {
    display: grid;
}

Grid 在很多方面与 flexbox 相似,但它旨在控制多轴布局,而不是单轴布局(垂直或水平空间)。

Grid 使您能够在具有 display: grid 的元素上编写布局规则,并引入了一些新的布局样式基元,例如 repeat()minmax() 函数。一个有用的 grid 单位是 fr 单位(它是剩余空间的一部分),您可以使用 3 个 CSS 属性构建传统的 12 列 grid,并在每个项目之间留有间距

.my-element {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 1rem;
}

上面的示例显示了单轴布局。Flexbox 主要将项目视为一个组,而 grid 使您可以精确控制它们在二维空间中的位置。我们可以定义此 grid 中的第一个项目占用 2 行和 3 列

.my-element :first-child {
  grid-row: 1/3;
  grid-column: 1/4;
}

grid-rowgrid-column 属性指示 grid 中的第一个元素从第一列跨越到第四列的起始位置,然后从第一行跨越到第三行。

流式布局

如果不使用 grid 或 flexbox,您的元素将以普通流式布局显示。当处于普通流式布局中时,您可以使用多种布局方法来调整项目的行为和位置。

内联块

还记得周围的元素如何不考虑内联元素上的块级边距和内边距吗?使用 inline-block,您可以使其发生这种情况。

p span {
    display: inline-block;
}

使用 inline-block 为您提供了一个框,该框具有块级元素的一些特征,但仍以内联方式随文本流动。

p span {
    margin-top: 0.5rem;
}

浮动

如果您有一张图片位于一段文本段落中,那么让文本像您在报纸上看到的那样环绕该图片是不是很方便?您可以使用浮动来实现此目的。

img {
    float: left;
    margin-right: 1em;
}

float 属性指示元素“浮动”到指定的方向。此示例中的图片被指示向左浮动,这使得兄弟元素可以“环绕”它。您可以指示元素向 leftrightinherit 浮动。

多列布局

如果您有一个很长的元素列表,例如世界所有国家/地区的列表,则可能会导致用户大量滚动并浪费时间。它还可能在页面上创建过多的空白。使用 CSS 多列,您可以将其拆分为多列,以帮助解决这两个问题。

<h1>All countries</h1>
<ul class="countries">
  <li>Argentina</li>
  <li>Aland Islands</li>
  <li>Albania</li>
  <li>Algeria</li>
  <li>American Samoa</li>
  <li>Andorra</li>
  …
</ul>
.countries {
    column-count: 2;
    column-gap: 1em;
}

这会自动将长列表拆分为两列,并在两列之间添加间距。

.countries {
    width: 100%;
    column-width: 260px;
    column-gap: 1em;
}

除了设置内容拆分成的列数之外,您还可以使用 column-width 定义所需的最小宽度。随着视口中可用空间的增加,将自动创建更多列;而随着空间的减少,列也会减少。这在自适应 Web 设计上下文中非常有用。

定位

布局机制概述的最后一项是定位。position 属性更改元素在文档普通流中的行为方式,以及它与其他元素的关系。可用选项为 relativeabsolutefixedsticky,默认值为 static

.my-element {
  position: relative;
  top: 10px;
}

此元素在其文档中的当前位置的基础上向下移动了 10 像素,因为它相对于自身定位。将 position: relative 添加到元素也会使其成为任何具有 position: absolute 的子元素的包含块。这意味着,当子元素应用了绝对定位时,它现在将被重新定位到此特定元素,而不是最顶层的相对父元素。

.my-element {
  position: relative;
  width: 100px;
  height: 100px;
}

.another-element {
    position: absolute;
    bottom: 0;
    right: 0;
    width: 50px;
    height: 50px;
}

当您将 position 设置为 absolute 时,它会将元素从当前文档流中移除。这意味着两件事

  1. 您可以使用 toprightbottomleft 在其最近的相对父元素中随意定位此元素。
  2. 绝对定位元素周围的所有内容都会重新流动,以填充该元素留下的剩余空间。

具有 position 值为 fixed 的元素的行为方式与 absolute 类似,其父元素是根 <html> 元素。固定定位元素会根据您设置的 toprightbottomleft 值从左上角保持锚定。

您可以使用 sticky 来实现 fixed 的锚定、固定方面,以及更可预测的、尊重文档流的 relative 方面。使用此值,当视口滚动经过元素时,它会保持锚定到您设置的 toprightbottomleft 值。

总结

CSS 布局有很多选择和灵活性。要进一步深入了解 CSS FlexboxGrid 的强大功能,请继续学习接下来的几个模块。

检查您的理解情况

测试您的布局知识

display 属性执行哪 2 项操作?

确定 inlineblocknone
布局引擎需要知道此框是否应为全宽以及是否需要新行。
确定 grid 布局框架。
display 属性可以将 display 设置为 grid,但它与布局框架无关。
确定子元素应如何表现。
Flexbox 和 grid 对其子元素有自己的看法和新功能。
确定框是否应滚动。
那是 overflow 属性。

要将多个段落流入列中,哪个 CSS 属性最适合此任务?

display: grid
虽然 grid 可以将多个段落放入列中,但这些列将是它们自己的列,不会从一列流到下一列。
column-count
段落将从一列的末尾流入下一列的开头,就像杂志或报纸一样。
display: flex
虽然 flex 可以将多个段落放入列中,但这些列将是它们自己的列,不会像需要的那样从一列流到下一列。
float
再试一次!

如果一个块脱离了流式布局,这意味着什么?

它卡在了河边。
这里是 CSS 上下文,而不是地理。
它已被赋予 topleft 位置值。
仅拥有这些属性不会将框拉出流式布局。
它不再根据其兄弟元素的位置进行定位。
例如,具有 position: absolute 的框现在使用基于包含块的 x 和 y 坐标进行定位,而不是它与其他兄弟元素的顺序。

Flexbox 和 Grid 默认情况下会包裹其子元素吗?

正确
必须使用 flex-wrap: wraprepeat(auto-fit, 30ch) 选择加入。
错误
Flexbox 和 Grid 具有需要应用其他样式的特殊换行功能。