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