简介
长期以来,将元素居中放置在父元素的中心是一件非常棘手的事情。随着 CSS 的发展,我们可以使用越来越多的工具来解决这个问题。如今,我们有了更多的选择!
我决定编写本教程,以帮助您了解不同方法之间的权衡,并为您提供一套策略,以便在各种情况下处理居中问题。
老实说,这比我最初想象的要有趣得多😅。即使你已经使用 CSS 有一段时间了,我打赌你也能学到至少一种新策略!
Link to this heading
使用 auto margins 实现居中
我们要了解的第一个策略是最古老的策略之一。如果我们想使元素水平居中,可以使用设置为特殊值 auto 的边距来实现:
.element { max-width: fit-content; margin-left: auto; margin-right: auto;}
.element
.element
Reveal Margin
容器宽度:
100%
首先,我们需要限制元素的宽度;默认情况下,Flow 布局中的元素会水平展开,以填充可用空间,因此我们无法将全宽元素居中。
我可以用一个固定值(例如 200px)来限制宽度,但在这种情况下,我真正想要的是让元素围绕其内容收缩。fit-content 是一个神奇的值,它的作用正是如此。从本质上说,它使 "width "的行为与 "height "相似,因此元素的大小由其内容决定。
为什么我要设置max-width而不是width?因为我的目标是阻止元素水平扩展。我想限制它的最大尺寸。如果我用width来代替,就会把它锁定在这个尺寸上,当容器非常窄时,元素就会溢出。如果将 "容器宽度 "滑块一直向左拖动,就可以看到元素会随着容器的缩小而缩小。
现在我们的元素已经受限,可以使用auto margins将其居中。
我喜欢把auto margins想象成饥饿的河马。每个auto margins都会尽可能多地吞噬空间。例如,看看如果我们只设置 margin-left: auto 会发生什么:
.element { max-width: fit-content; margin-left: auto;}
.element
.element
Reveal Margin
容器宽度:
100%
当 margin-left 是唯一具有自动页边距的一侧时,所有额外的空间都会作为页边距应用到这一侧。当我们同时设置 margin-left: auto 和 margin-right: auto 时,两只河马会各自吞噬相等的空间。这就迫使元素居中。
还有:我一直使用 margin-left 和 margin-right,因为对它们很熟悉,但还有更好、更现代的方法:
.element { max-width: fit-content; margin-inline: auto;}
.element
.element
Reveal Margin
容器宽度:
100%
margin-inline 会将 margin-left 和 margin-right 设置为相同的值(auto)。它对浏览器的支持非常好,几年前就已在所有主流浏览器中支持使用。
逻辑属性
margin-inline 不仅仅是 margin-left + margin-right 的简写。它是一系列逻辑属性的一部分,旨在使网络国际化更加容易。
在英语中,字符从左到右书写在一条水平线上。这些字符组成单词和句子,并组合成 "块"(段落、标题、列表等)。语块从上到下垂直堆叠。我们可以将其视为英文网站的方向。
但这并不普遍!有些语言,如阿拉伯语和希伯来语,是从右到左书写的。其他语言,如中文,在历史上都是竖着写的,字符从上到下,方块从一边到另一边*。
逻辑属性的主要目的是创建一个抽象概念,使其凌驾于这些差异之上。我们可以使用 margin-inline-start 来代替为从左到右的语言设置 margin-left 和为从右到左的语言设置 margin-right。页边距将根据页面语言自动应用到正确的一侧。
尽管这种居中方法已经存在了很久,但我仍然发现自己经常会用到它!当我们想要将单个子代居中,而又不影响其任何同级子代时(例如,博文中位于段落之间的图片),这种方法尤其有用。
让我们继续我们的居中之旅。
Link to this heading
使用 Flexbox 实现居中
Flexbox 的设计为我们沿主轴分布一组项目提供了大量控制。它提供了一些非常强大的居中工具!
让我们从单个元素的水平和垂直居中开始:
.container { display: flex; justify-content: center; align-items: center;}
.element
容器宽度:
100%
容器高度:
100%
Flexbox 居中设置的最大优势在于,即使子元素无法容纳在容器中,它也能正常工作!试着缩小宽度/高度,注意元素会对称地溢出。
它还适用于多个子元素。我们可以使用 flex-direction 属性来控制它们的堆叠方式:
.container { display: flex; flex-direction: row; justify-content: center; align-items: center; gap: 4px;}
1
2
3
Flex Direction:
row
column
row-reverse
column-reverse
在本教程将要介绍的所有居中模式中,这可能是我用得最多的一种。它是万能的,也是一个很好的默认选项。
Link to this heading
在窗口(viewport)中居中
到目前为止,我们已经了解了如何在父容器中将元素居中。但如果我们想让元素在不同的上下文中居中呢?某些元素(如对话框、提示和 GDPR 横幅)需要在视口中居中。
这是定位布局的范畴,是一种布局模式,当我们想要将某些元素从流程中移除并将其锚定到其他位置时就会用到。
下面就是这种情况:
.element { position: fixed; inset: 0px; width: 12rem; height: 5rem; max-width: 100vw; max-height: 100dvh; margin: auto;}
.element
.element
容器宽度:
100%
容器高度:
100%
在我们将要讨论的所有策略中,这可能是最复杂的一个。让我们来分析一下。
我们使用 position:fixed,将该元素锚定到窗口(viewport)。我喜欢把窗口想象成网站前方的一块玻璃,就像火车的车窗,可以显示滚动的风景。position:fixed的元素就像一只落在窗口上的瓢虫。
接下来,我们要设置 inset: 0px,这是一个简写,它将top、left、right和buttom都设置为相同的值,即 0px。
仅使用这两个属性,元素就会拉伸以填充整个视口,并从每个边缘开始增长至距边缘 0px。这在某些情况下可能有用,但并不是我们的目的。我们需要对其进行限制。
我们选择的具体值会因具体情况而异,但一般来说,我们要设置默认值(width和height)以及最大值(max-width和max-height),这样元素就不会在较小的窗口中溢出。
这里有一个有趣的现象:我们设置了一个不可能的条件。我们的元素不可能左起 0px,右起 0px,宽度也不可能只有 12rem(假设窗口宽度大于 12rem)。我们只能两个都选:
.element { position: fixed; left: 0; right: 0;}
选择两个:
left: 0px
right: 0px
width: 12rem
如果我们希望元素的左侧边缘为 0px,右侧边缘为 0px,那么它将拉伸到视口的全宽,也就是宽于 12rem 的宽度。
CSS 渲染引擎通过优先级来解决这一矛盾。它会听从width限制,因为这看起来很重要。如果不能同时锚定到左侧和右侧,它就会根据页面的语言选择一个选项;因此,在像英语这样从左到右的语言中,它会沿着左侧边缘放置。
但是!当我们把老朋友 margin: auto 加入其中时,有趣的事情发生了。它改变了浏览器解决不可能条件的方式;它不再锚定到左边缘,而是将其居中。
而且,与 Flow 布局中的自动页边距不同,我们可以用这一技巧将元素水平和垂直居中。
.element { position: fixed; inset: 0px; width: 12rem; height: 5rem; max-width: 100vw; max-height: 100dvh; margin: auto;}
.element
.element
Reveal Margin
容器宽度:
100%
容器高度:
100%
要记住的东西很多,但这个技巧有 4 个关键要素。
固定定位
锚定到所有 4 个边缘, inset: 0px
限制宽度和高度
Auto margins
我们也可以使用同样的技巧,将某些内容置于单一方向的中心位置。例如,我们可以制作一个 GDPR cookie 横幅,横幅水平居中,但锚定在视口底部附近:
.element { position: fixed; left: 0px; right: 0px; bottom: 8px; width: 12rem; max-width: calc( 100vw - 8px * 2 ); margin-inline: auto;}
我们重视您的隐私数据。
我们使用 cookie 向广告商出售这些数据,以增强您的浏览器体验。这是非常有价值的。
听起来不错
容器宽度:
100%
容器高度:
100%
通过省略 top:0px 后,我们就消除了垂直方向上的不可能条件,横幅就固定在底部边缘了。作为一个很好的点缀,我使用了calc函数来箝位最大宽度,这样元素周围总是有一些缓冲区。
我还将 margin: auto 换成了 margin-in-line:auto,严格来说这不是必须的,但感觉更精确。
Link to this heading
如何居中未知尺寸元素
上述方法要求我们赋予元素一个特定的大小,但如果我们不知道它应该有多大?
过去,我们不得不借助变换技巧来实现这一目标,但幸运的是,我们的朋友 fit-content 也能在这方面提供帮助!
.element { position: fixed; inset: 0; width: fit-content; height: fit-content; margin: auto;}
A
容器宽度:
100%
# 字符数:
1
这将使元素围绕其内容缩小。如果要限制最大宽度,我们仍然可以设置最大宽度(例如 max-width: 60vw),但我们不需要设置最大宽度;元素会自动保持在视口内。
Link to this heading
使用 CSS 网格(Grid)实现居中
我所知道的最简洁的水平和垂直居中方法就是 CSS Grid:
.container { display: grid; place-content: center;}
.element
容器宽度:
100%
容器高度:
100%
place-content 属性是 justify-content 和 align-content 的简写,对行和列应用相同的值。结果是一个 1×1 的网格,单元格位于父容器的正中间。
Link to this heading
与 Flexbox 的区别
这种解决方案看起来很像我们的 Flexbox 解决方案,但需要注意的是,它使用的是完全不同的布局算法。在我的工作中,我发现 CSS 网格方案并不像 Flexbox 方案那样普遍有效。
例如,请考虑以下设置:
.container { display: flex; justify-content: center; align-items: center;}.element { width: 50%; height: 50%;}
.element
Layout Algorithm:
flexbox
grid
很奇怪吧?为什么 CSS 网格版本会变得这么小?
事情是这样的:子元素设置 width: 50%,height: 50%。在 Flexbox 中,这些百分比是根据父元素 .container 计算出来的,而这正是我们想要的。
而在 CSS 网格中,百分比是相对于网格单元格而言的。我们的意思是,子元素的宽度应为其列的 50%,高度应为其行的 50%。
实际上,我们并没有明确给出行/列的大小;我们也没有定义 grid-template-columns 或 grid-template-rows.如果我们省略了这些信息,网格轨道将根据其内容计算大小,将每一行/列中的任何内容都收缩起来。
最终结果是,我们的网格单元大小与 .element的原始大小相同,然后元素缩小到该网格单元的 50%:
.container { display: grid; place-content: center;}.element { width: 50%; height: 100%;}
.element
元素宽度:
50%
元素高度:
100%
我想说的是,CSS 网格是一种复杂的布局算法,有时,额外的复杂性会妨碍我们的工作。我们可以添加更多 CSS 来修复这段代码,但我认为使用 Flexbox 更简单。
Link to this heading
将元素堆叠居中
CSS Grid为我们提供了更多居中的超级功能。利用 CSS 网格,我们可以将多个元素分配到同一个单元格中:
.container { display: grid; place-content: center;}.element { grid-row: 1; grid-column: 1;}
.element
.element
.element
.element
# 元素个数:
1
我们仍然有一个 1×1 的网格,只不过现在我们通过 grid-row / grid-column 在单元格中塞入了多个子单元。
如果还不清楚,下面是这种设置的 HTML 简图:
html
在其他布局模式中,元素会水平或垂直堆叠,但在 CSS 网格设置中,元素会前后堆叠,因为它们都被告知要共享相同的网格空间。很酷吧?
令人难以置信的是,即使子元素的尺寸不同,这种方法也能奏效!看看这个
.container { display: grid; place-content: center; place-items: center;}.element { grid-row: 1; grid-column: 1;}
.element
.element
.element
.element
Reveal Grid
# 元素个数:
1
在此演示中,添加了红色虚线来显示网格行和列。请注意,它们会扩展到包含最大的子单元格;添加所有元素后,单元格的宽度与粉色天际线图像相当,高度与彩色太空图像相当!
我们还需要一个属性:place-items: center。place-items 是 justify-items 和 align-items 的简写,这些属性控制网格单元格中图片的对齐方式。
如果没有该属性,网格单元格仍将居中,但该单元格内的图像将全部堆叠在左上角:
.container { display: grid; place-content: center;}.element { grid-row: 1; grid-column: 1;}
.element
.element
.element
.element
Reveal Grid
place-items:
start (default)
center
这是非常先进的东西!
Link to this heading
文本居中
在 CSS 中,文本是一个特殊的东西。我们无法使用本篇文章中探讨的技术来影响单个字符。
例如,如果我们尝试使用 Flexbox 使一个段落居中,我们将使文本块居中,而不是文本本身居中:
.container { display: flex; justify-content: center; align-items: center;}
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s.
容器宽度:
100%
Flexbox 使段落在窗口中居中,但并不影响单个字符。它们仍然保持左对齐。
我们需要使用文本对齐方式text-align将文本居中:
.container { display: flex; justify-content: center; align-items: center; text-align: center;}
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s.
容器宽度:
100%
Link to this heading
居中技术前瞻
前面,我们介绍了如何在 Flow 布局中使用自动页边距使元素水平居中。如果我们希望该元素也垂直居中,就需要切换到不同的布局模式,如 Flexbox 或 Grid。
......还是我们?
看看这个
.container { align-content: center;}.element { max-width: fit-content; margin-inline: auto;}
.element
容器宽度:
100%
容器高度:
100%
align-content 是 CSS 网格的一种,但我们并没有在这里设置 display: grid。这到底是怎么回事?
关于 CSS,我最大的感悟之一就是它是布局算法的集合。我们编写的属性是这些算法的输入。align-content 最早在 Flexbox 中实现,在 CSS Grid 中发挥了更大的作用,但它并没有在默认布局算法 Flow layout 中实现。直到现在。
当我在 2024 年初写下这篇文章时,浏览器供应商正在 Flow 布局中实施 align-content,以便控制内容的 "块 "方向对齐。现在还为时尚早;这一新行为仅在 Chrome Canary(在标志后面)和 Safari 技术预览版中可用。
(需要说明的是,上面的演示是假的。我在 Chrome Canary 和 Safari 技术预览版中体验了新的对齐内容支持,然后使用 Flexbox 重新创建了完全相同的行为。请原谅我的欺骗!)
这真的有用吗?
据我所知,就我们可以创建的用户界面类型而言,这个新选项并没有开启任何新的大门。我们已经可以使用本教程中探讨的技术重现相同的行为。
不过,我还是很期待它能得到广泛应用。一直以来,我都觉得为了居中而翻转到一个完全独立的布局模式有点傻。
Link to this heading
超越模式
因此,多年来,我一直把 CSS 当成是模式的集合。我从大脑中粘贴出一堆记忆片段,以解决我当前面临的任何问题。
这样做效果还不错,但确实感觉很受限制。时不时地,事情就会莫名其妙地中断;我用过几百次的代码片段会突然出现不同的表现。
当我花时间深入学习 CSS 时,我对这门语言的体验完全改变了。很多事情都迎刃而解了。我不再依赖于死记硬背的代码片段,而是可以依靠自己的直觉!✨
在本教程中,我们探讨了一些有用的居中模式,希望下次需要居中时它们能派上用场。不过说实话,我们只是触及了皮毛;我们还有很多方法可以使用现代 CSS 来居中!我认为,与其死记硬背更多的代码片段,不如建立一个关于 CSS 工作原理的强大心智模型,这样我们就能随时提出解决方案!
Last Updated
February 14th, 2024