CSS 布局:从浮动到弹性框和网格的历史

2025-06-08

CSS 布局:从浮动到弹性框和网格的历史

由于弹性框和网格的出现,CSS 布局在过去几年中发生了显著的变化。为了弥合像我这样在 CSS 时代之前就已入行的开发者与现在刚入行的开发者(那些对 CSS 时代充满好奇的人clearfix)之间的差距,让我们回顾一下我们过去的解决方案,并将它们与如今(更简单、更灵活)的方法进行比较。虽然开发新手不太可能使用旧方法,但如果您开发的是老网站,您仍然会遇到这些布局。

特别感谢 Rachel Andrew、Jen Simmons、CSS Tricks、MDN 和 Scrimba 提供了大量关于此主题的在线资源和书籍

最初于 2019 年 11 月 24 日发布在 Codepen 上。

过去、现在和未来

黎明前(无 CSS / 表格)

在出现 CSS 之前,所有的“样式”都是使用 HTML 完成的,因为没有办法将其分离。

  • 文档流——HTML 中元素的顺序
  • 布局表
    • 内容和呈现不分离
    • 更新复杂/耗时
    • 没有响应
    • <table>标签的语义使用不正确

我曾经不得不修改过很多完全用表格构建的网站,这真是令人不快。有一次,为了在一个并非我本人设计的网站上完成设计,我把一个完整的表格塞进了单元div格里td。(虽然我并不满意,但最终还是成功了,并且通过了 W3C 的验证!)

浮动和黑客时代

浮动最初是设计用来将文本环绕在图像或其他元素周围的。后来,一些非常聪明的开发者想出了一种破解浮动工具的方法,通过设置容器内浮动元素的宽度来实现多列布局。

浮动适用于高度相等的列:

不幸的是,当列高度不同时,浮动效果不好。在上面的例子中,由于第二张卡片较长,它阻碍了第四张卡片在第一张卡片正下方形成新行。

解决方案是在 HTML 中使用 clearfix 为每一行添加一个包装器。这使得每一行都成为自己的容器:

Bootstrap 3 的网格系统遵循以下逻辑:

  • 行左右两侧的边距均为负数,以适应浮动 div 的左右填充
  • 此方法不适用于动态内容,或者当您不知道每行有多少个项目时

当元素浮动时,没有简单的方法可以垂直对齐内容。

允许垂直对齐的另一种方法是内联块:

然而,使用内联块元素进行布局存在一些复杂问题:

  • 内联元素同时包含和的属性blockinline这意味着您可以设置宽度(因为block属性),但始终保留水平空白的项目inline-block以保持单词之间的空格(因为属性inline
  • 这意味着项目之间总是存在固有的余量
  • 您要么需要将边距清零,要么确保宽度之和不超过 100%

我怎样才能使我的身高匹配???

浮动和内联块都不会强制元素具有相同的高度。

让我们把所有东西重新变成一张桌子?

这些解决方案都不是理想的,也不需要组合不同的 CSS 属性。我刚开始使用浮动,并且相当熟练,但在过去几年里完全避免使用它之后,我一点也不怀念它了。

现代时代——弹性和网格

弹性盒和网格是针对我们之前布局方法中遇到的问题的现代解决方案。这两种布局各有特色,但让我们先理清一些事情:

  • Flexbox 最先被实现
  • 网格的加入并不意味着Flex 会被淘汰。有些功能在 Flex 中可以实现,但在网格中却无法实现。
  • 两者有重叠之处,可以达到相同的视觉效果
  • 如果可以的话,使用正确的方法来利用其独特的固有功能
  • 你可以使用它们来完成它们单独无法完成的事情

何时使用 Flex 还是 Grid

在以下情况下使用 Flex:

  • 内容优先
  • 需要水平或垂直对齐
  • 布局是一维的
  • 需要更好的旧浏览器支持(但使用前缀!)

在以下情况下使用网格:

  • 无论内容如何,​​都需要设置宽度
  • 需要二维布局(一行或一列中的项目需要与前一行或前一列中的项目对齐)
  • 元素需要重叠

如果您对这些布局方法完全陌生,那么“一维”和“二维”这两个术语会让您非常困惑,因为您可以使用 flexbox 来拥有多行或多列,而只能对一行或一列使用网格。

因为 flex 每次只考虑一个方向,所以我认为 flex 会忘记之前的操作。如果你有两行使用了 flex 的元素,那么当你移动到第二行时,flexbox 就不会记得它在第一行做了什么。它会遵循已设置的 flex 属性;如果这恰好导致第二行的元素与第一行的元素对齐,那只是恰好它们的宽度相同。它们不一定非要对齐。

网格就像四子棋的棋盘。你可以放下塑料棋子,将多个红色棋子排列在一起,形成一个大的红色区域,或者巨大的蓝色区域,但棋子必须始终位于行和列内。任何棋子都不能填满半行或半列。

弹性盒子

  • 内容驱动
  • 实现更轻松的垂直和水平对齐
  • 允许子元素“伸缩”,“要么增长以填充未使用的空间,要么收缩以避免溢出父元素” - W3
  • 仅考虑一个方向(行或列),并且一次只处理一个方向

您可以设置弹性项目的宽度,但如果您要设置弹性容器中所有内容的宽度,那么您就没有利用弹性,而应该使用网格。

当需要垂直对齐容器内的内容时,Flexbox 是个救星。我以前常用的方法是:

  • 将行高设置为等于容器的高度(因此如果文本换行到多行,就会爆炸)
  • 在物品周围创建一个包装器,并使用display: table和的组合display: table-cell
  • 使用transform: translateY()

弹性盒子示例

让我们看一下默认的 Flex 设置,并与 flex-grow 和 flex-shrink 的不同值进行比较:

还记得我们不能同时实现等高容器和垂直对齐吗?使用 flexbox 就可以:

如果每行的项目数量不一致,我们可以尝试使用 flex-grow 和 flex-shrink 来填充行。这是一个 flex 忘记之前操作的例子;它只遵循你设置的内容大小规则。

下面是两个使用 flex 布局的简单导航栏,很好地展现了 flex 相对于网格的优势。每个 flex 元素都延伸到内容的宽度;网格布局在这里毫无用处。第一个导航栏将链接分组到左侧,并将一个链接对齐到最右侧以将其分开:

当我们不知道有多少个链接或链接名称有多长时,第二个导航栏允许所有项目等距排列。使用浮动或内联块元素并不难实现;只是每次添加新项目或更新文本时都需要手动更新边距:

最后,我们用 flex 搭建了一个砖石风格的画廊。用 grid 无法完全实现这一点,因为所有使用 grid 的东西都必须与设置的水平和垂直轨道对齐:

网格

Grid 是第一个专为布局设计的 CSS 模块——自从 CSS 创建以来,我们一直缺少它。

网格可以处理行和列,这意味着它始终会将项目与你设置的水平和垂直轨道对齐。网格主要在容器上定义,而不是像弹性盒子那样在子元素上定义。

网格示例

这是一个使用小数单位的基本非响应式布局。注意,与之前的方法相比,代码简洁得多。

请注意,同样的布局也可以通过 flexbox 实现,只要你为所有 flex 子元素设置 width 属性即可。这将导致它们不再增长或收缩(或 flex),这意味着你不再利用 flex 的固有属性。这仍然比使用浮动或内联块元素更好,但如果在这种情况下可以使用网格,那么使用网格会更好。

以下是使用自动调整和重复的响应式布局。请注意,它缺少媒体查询:

以下是类似的想法,只不过是用于照片库。请注意,对于不支持网格的老版本浏览器,可以使用功能查询来实现回退:

高级网格示例

我们可以通过指定行/列号以及它们的起始/终止位置来控制项目的确切位置。如果您习惯于其他编程语言(数组从 0 开始),这种方法可能会让您感到困惑。使用 grid,每行都从 1 开始。我强烈建议使用 Firefox 或 Chrome 的开发者工具来轻松调试这些布局:

这是完全相同的布局,但我们不使用网格和行号,而是使用命名区域:

因为我们可以使用列和行跨度来控制项目的精确大小,所以我们可以使用 grid 属性创建上面 flex masonry 画廊的变体grid-auto-flow: dense。请注意,这会重新排列项目,使其与 html 源顺序中的布局方式不同。

Flex 和 Grid 的超级组合

这个布局使用网格来对齐卡片,并使它们在所有行上的高度相等(终于!)。然后使用弹性框将按钮与每张卡片的底部对齐。现在看起来很简单,但这是我使用浮动时最令人沮丧的经历之一;我必须设置浮动min-height,并希望内容之后不会发生剧烈波动,但这意味着需要使用媒体查询多次更改高度。

其他布局方法

尽管 Flexbox 和 grid 非常棒,但它们并不会取代之前的所有方法。HTML 的文档流仍然是原始布局。我们可能不会在 body 标签上采用这种布局。其他布局方法只要我们能将其用于实际用途display: grid,也同样适用

  • 使用浮动将项目环绕另一个项目(例如:文本环绕图像)
  • 使用表格来存储表格数据(从语义上来说也更好)
  • 对于必须脱离 HTML 流的任何元素,请使用位置:absolute / fix / sticky
  • 使用多列布局来呈现连续流动的内容

向后兼容性

但是[过时的浏览器]怎么办?

Flexbox 和 grid 仅在现代浏览器中得到完全支持。Internet Explorer 10 支持 Flexbox,但对某些属性(尤其是 flex 简写)有一些限制;IE11 有自己的 grid 前缀,并且不支持所有其他属性。从 2021 年起,你可能不再需要支持 IE 了。

我以前觉得为老旧浏览器编写网页代码非常麻烦,尤其是 IE9 及以下版本。现在偶尔还是会感到沮丧,但最近我更认同网站应该尽可能向后兼容的观点,因为这正是网络存在的意义。网站不像应用程序那样需要版本控制,如果浏览器版本过低就无法完全运行。网站应该尽可能地保持可访问性。

我在 IE 中测试时犯的一个错误是试图让设计在所有桌面浏览器上都完全一致。然后我不断听到有人提出这样的想法:为什么网站在 IE8 和现代浏览器中的外观必须完全一样?我们在移动设备和平板电脑上的体验已经有所不同,即使是同一款浏览器在不同的电脑上呈现的效果也会略有不同。

如果你的客户主要使用 IE11,那么你不应该把所有东西都用网格布局。理想情况下,设计时也应该考虑到这一点。但如果我们是基于现代浏览器进行设计,那么我们应该使用现代方法,要确保这些方法在所有老版本浏览器中都能正常显示和运行。

布局比较

让我们采用一开始的相同的简单的 3 框布局,看看它们在浮动、弹性和网格方面的比较情况:

请注意,浮动方法不会使项目的高度相等——除非你添加一个min-height,即使添加了,如果其中一个框中的内容变长,你也会回到原点。对于老版本的浏览器来说,这或许没问题;它们可能无法完美地呈现按钮的对齐效果,但内容、图片和顺序仍然以相同的方式呈现,并且所有内容仍然可读。

弹性 CSS

我非常喜欢 Jen Simmons 提出的“弹性 CSS”这个术语。网站的 CSS 应该在失败和覆盖方面正确使用级联。浏览器无法识别的 CSS 属性将不会运行,并跳到下一行。

了解了这一点,我们就可以以最大化兼容性的方式安排 CSS。如果我们使用特性查询,则需要考虑到现代浏览器会运行所有 CSS,包括我们为旧版浏览器设计的 CSS。因此,如果我们在旧版浏览器的 CSS 属性上首先设置了 width 属性(假设我们正在使用display: inline-block),并且我们打算在现代浏览器中在同一元素上使用 grid 属性,那么我们需要在特性查询中覆盖该 width 属性,因为现代浏览器会运行所有 CSS。我们必须确保“取消”或撤消其他 CSS。

回顾一下这个例子,看看我在说什么:

浏览器兼容性是把双刃剑,虽然令人沮丧,但我很感激大家仍然可以浏览20年前的网站,这也是网络如此特别的原因之一。我曾与同事讨论过,既然我们必须花费更多时间编写额外的CSS来兼容老旧浏览器,为什么还要这么快就转向这些新方法呢?

作为一名开发者,这取决于个人偏好——你希望在预算范围内打造一个网站,还是在 Web 开发领域实现长期发展。但这正是 Web 开发的本质,事物瞬息万变,很难跟上一切,但这些 CSS 布局方法与我们之前使用的工具有着根本的不同,也更加优秀。随着 flexbox 和 grid 不断添加新属性,学习这些新方法的投资终将获得回报,并使未来的 Web 更加美好。

如果我们使用更新的方法,我们可以:

  • 紧跟 Web 开发领域
  • 更轻松地构建更复杂的布局
  • 使网站将来更易于维护
  • 仍然保持对旧版浏览器的向后兼容性

开始逐步使用较新的方法并浏览测试组件,您将走上令人惊叹的新设计之路。

更多信息

免费课程和工具

鏂囩珷鏉ユ簮锛�https://dev.to/dianale/css-layouts-history-from-float-to-flexbox-and-grid-5af7
PREV
为什么使用 Swagger 创建和记录 API
NEXT
SOLID 原则变得简单