前言
长期以来,如何在父元素中居中对齐一个元素,一直是一个让人头疼的问题,随着 CSS 的发展,越来越多的工具可以用来解决这个难题,五花八门的招式一大堆,这篇博客,旨在帮助你理解不同的居中方法,并提供一整套策略,帮助你在各种场景下进行居中对齐。说实话,这比我最初想象的要有趣得多 😅。不管你是技术大牛,还是萌新小白,我相信至少有一种是适合你的!
自动边距居中对齐
我们首先来看一种最简单的,应该也是大家使用最多的,如果我们想要水平居中对齐一个元素,可以使用设置为边距 auto 来实现:
.element {
max-width: fit-content;
margin-left: auto;
margin-right: auto;
}
这种方式首先需要限制元素的宽度默认情况下,流式布局中的元素会水平扩展以填充可用空间,所以无法真正居中一个满宽的元素,可以通过固定宽度来约束元素(例如 200px),但实际上想要的效果是让元素根据其内容进行自适应,fit-content
是一个神奇的值,它正好能实现这一点,它基本上能让“width”属性的表现效果类似于“height”,使得元素的尺寸由其内容决定。
为什么要设置 max-width
而不是 width
?因为目标是限制元素的最大宽度,如果使用 width
,则元素会被固定在那个大小上,当容器非常窄时,元素可能就会溢出。
当元素的宽度已经固定之后就可以通过设置边距的auto属性来进行居中对齐。每个属性为auto的边距都会尽可能地占据元素空间。例如如果只设置 margin-left: auto
会发生什么:
.element {
max-width: fit-content;
margin-left: auto;
}
当只有 margin-left 设置为 auto 时,所有的多余空间都会分配到左边距上。而当同时设置 margin-left: auto 和 margin-right: auto 时,两个边距会均等地分配空间,这样元素就会被居中对齐,我之所以一来就写 margin-left 和 margin-right 是因为它们比较常见,但实际上还有一种更理想的方法来实现居中对齐:
.element {
max-width: fit-content;
margin-inline: auto;
}
margin-inline 直接设置水平方向的边距他可以设置与 margin-left 和 margin-right 相同的值(如 auto),并且兼容性也相当完美,几年前就已经被所有主要浏览器所支持!
margin-inline
不仅仅是margin-left
和margin-right
的简写,它是逻辑属性集合的一部分,旨在简化网络国际化过程。在英语中,字符是从左到右水平书写的。这些字符组成单词和句子,形成“块”(如段落、标题、列表等)。这些块垂直堆叠,从上到下排列。这可以视为网站的书写方向,然而,这并不是全球通用的!一些语言,如阿拉伯语和希伯来语,是从右到左书写的。其他语言,如中文,在历史上书写方式是纵向书写的,字符从上到下排列,块则从侧面排列。逻辑属性的主要目标是创建一个抽象层,超越这些差异,与其分别为从左到右的语言设置
margin-left
,而为从右到左的语言设置margin-right
,不如使用margin-inline-start
。这样,边距会根据页面的书写方向自动调整到正确的一侧。
虽然这种方法已经算比较老了,但就我自己而言是经常使用它的!如果想将单个子元素居中而不影响其任何兄弟元素时,这种方法特别有用(例如,博客文章中段落之间的图像)。
Flexbox 居中
Flexbox 的设计旨在提供对沿主轴分布一组项目的强大控制。它提供了一些非常强大的工具来实现居中对齐!
首先,我们来看如何使用 Flexbox 将单个元素在水平和垂直方向上都居中对齐:
.container {
display: flex;
justify-content: center;
align-items: center;
}
Flexbox 居中有一个特点就是即使子元素超出了容器的范围,它仍然能正常工作!尝试缩小容器的宽度或高度,不难发现元素就算是溢出了也是对称地溢出!!
此外,这种方法同样适用于多个子元素,可以使用 flex-direction
属性的row / column / row-reverse / column-reverse 这四个属性值来控制子元素的排列方式:
.container {
display: flex;
flex-direction: row / column / row-reverse / column-reverse;
justify-content: center;
align-items: center;
gap: 4px;
}
在多种居中对齐的方式中这个方式可能是我最常使用的,它是一个非常通用的选择!
在视口内居中对齐
到目前为止,本文讨论了如何在父容器内居中对齐一个元素。但是如果我们想要在其他上下文中居中对齐元素呢?例如,弹窗、提示框等元素需要在视口内居中对齐呢?这就涉及到定位布局(positioned layout),当想要将元素从正常文档流中抽离,并将其固定到某个位置时使用的布局模式。
下面是这种布局的效果示例:
.element {
position: fixed;
inset: 0px;
width: 12rem;
height: 5rem;
max-width: 100vw;
max-height: 100dvh;
margin: auto;
}
到现在为之所有提到的方法中,这种方式可能是最复杂的一种具体逐步解析如下:
使用 position: fixed 将元素固定在视口中。视口可以比作一块挂在网站前面的玻璃窗,就像火车窗户显示着外面的风景。使用 position: fixed 的元素就像窗花一样。 接下来设置 inset: 0px,这是一个简写属性,将 top、left、right 和 bottom 全部设置为 0px,仅使用这两个属性,元素会伸展以填充整个视口,使其离每个边缘都为 0px。这在某些情况下可能有用,但这里的目标是限制元素的尺寸,具体选择的值会根据实际情况有所不同,但通常需要设置默认值(如 width 和 height),以及最大值(如 max-width 和 max-height),以确保元素在较小视口中不会溢出。
不过有一个有趣的现象:如果设置了一个不可能的条件就比如元素不能同时离左边缘 0px 和右边缘 0px,同时宽度仅为 12rem(假设视口宽度大于 12rem)的话只能选择其中两个条件:
如果将元素固定在左边缘,并设置其宽度为 12rem这样的话它就无法同时离右边缘 0px。
CSS 渲染引擎通过优先级来解决这个矛盾,它会优先考虑宽度限制,如果无法同时固定在左边缘和右边缘,它会根据页面当前的语言环境来选择一个选项;例如,在像英语这样的从左到右的语言中,元素会靠近左边缘。
BUT!当引入 margin: auto
时,就会发生些有意思的变化,这会改变浏览器解决不可能条件的方式,元素不再固定在左边缘,而是被居中对齐。
不同于流式布局中的自动边距,这个技巧可以用来同时在水平和垂直方向上居中对齐元素。
.element {
position: fixed;
inset: 0px;
width: 12rem;
height: 5rem;
max-width: 100vw;
max-height: 100dvh;
margin: auto;
}
要记住这些要点可能会有些复杂,但这个方式的关键有四个要素:
- 固定定位(Fixed Positioning)
- 通过
inset: 0px
固定到四个边缘 - 限制宽度和高度
- 自动边距(Auto Margins)
这个方式也可以用来在单个方向上进行居中对齐例如,可以使用相同的方法创建一个水平居中的 通知同时将其固定在视口的底部附近:
.element {
position: fixed;
left: 0px;
right: 0px;
bottom: 8px;
width: 12rem;
max-width: calc(
100vw - 8px * 2
);
margin-inline: auto;
}
通过省略 top: 0px 后,移除了垂直方向上的不可能条件,使得通知固定在底部边缘为了使其看起来更加美观,这里还使用了 calc 函数来设置最大宽度,这样元素周围始终有一定的缓冲空间, 另外上述方法需要给元素指定具体的大小。如果不知道元素的确切尺寸,如何处理呢? 我相信不少小伙伴通常会使用 transform 技巧来解决这个问题,但现在可以利用 fit-content 来达到相同效果!
.element {
position: fixed;
inset: 0;
width: fit-content;
height: fit-content;
margin: auto;
}
这样会使元素根据其内容进行缩小。如果需要,可以设置最大宽度(max-width
)来进行限制,但并不强制要求设置最大宽度,元素会自动保持在视口范围之内。
Grid布局
通过 CSS Grid,可以非常方便地实现元素的水平和垂直居中对齐。
.container {
display: grid;
place-content: center;
}
place-content 属性是 justify-content 和 align-content 的简写,将相同的值应用于行和列。这样会生成一个 1×1 的网格,其中单元格正好位于父容器的中央。 这种解决方案看起来与 Flexbox 方案非常相似,但需要注意的是它使用的是完全不同的布局算法。在实际工作中实际上Grid布局 方案在某些情况下的效果不如 Flexbox 方案那么普遍适用。例如以下设置:
没想到吧?为什么Grid布局之后文本内容变得这么小?
问题就在于:子元素的 width: 50% 和 height: 50% 是相对于网格单元格的。与 Flexbox 不同,在 Flexbox 中,这些百分比是基于父元素 .container 的。 在 Grid布局中,这些百分比是相对于网格单元格的,也就是说子元素的宽度是其所在列的 50%,高度是其所在行的 50%, 由于没有为行和列指定明确的尺寸,也没有定义 grid-template-columns 或 grid-template-rows,网格轨道会根据内容自动计算其尺寸,即“收缩包裹”在每个行/列中的内容。最终网格单元格的尺寸与 .element 的原始尺寸相同,然后元素会缩小到该网格单元格的 50% 大小。
这是一个非常复杂的话题,为了不偏离本次主题,也就不继续展开了。重点是Grid布局是一个复杂的布局算法,有时这种额外的复杂性会带来未知的bug,虽然可以通过添加更多 CSS 来修复这个问题,但使用 Flex
居中对齐多个元素
CSS Grid 还提供了另一个强大的居中功能。通过 CSS Grid,可以将多个元素放置在同一个单元格中,这样就能轻松实现多个元素的居中对齐。
依然使用 1×1 的网格,只不过现在将多个子元素放置在同一个单元格中,通过 grid-row
和 grid-column
来实现。为了更清楚地说明这种设置,下面是一个简单的 HTML 示例:
<div class="container">
<img class="element" />
<img class="element" />
<img class="element" />
<img class="element" />
</div>
在其他布局模式下,元素通常会水平或垂直堆叠,但使用Grid 布局设置时,元素会在同一个网格空间内前后堆叠,这样它们就会重叠在一起,即使子元素的尺寸不同,这种方法仍然有效!来看一下这个示例:
在这个示例中,添加了红色虚线来显示网格的行和列,它们会自动扩展以容纳最大的子元素;所有元素添加后,结果单元格的宽度和高度与图片相同!为了确保不会出现任何问题,还需要一个额外的属性:place-items: center
,place-items
是 justify-items
和 align-items
的简写,这些属性控制网格单元格内图片的对齐方式。如果没有这个属性,虽然网格单元格仍会居中,但单元格内的图片会堆叠在左上角。
文本居中
文本在 CSS 中是一个特殊的情况,无法使用上面提到的任何方式来影响单个字符的对齐方式。
例如如果尝试使用 Flexbox 来居中一个段落,我们只能居中整个文本块,而不是文本本身:
Flexbox 可以将段落在视口内居中,但它不会影响文本中的单个字符,字符仍然保持左对齐。
为了居中文本内容,需要使用 text-align
属性:
更好的居中方式
之前我们看到,使用自动边距可以在流式布局中水平居中元素,如果希望该元素垂直居中,则需要切换到其他布局模式,例如 Flexbox 或 Grid。
接下来看看这个方法:
这是什么情况呢?align-content
是 CSS Grid 的属性,但我们在这里并没有设置 display: grid
。这是怎么回事?
其实 CSS 它实际上是一系列布局算法的集合,编写的属性是这些算法的输入,align-content
最早在 Flexbox 中实现,并在 CSS Grid 中发挥了更大的作用,但它并未在默认的流式布局(Flow layout)算法中实现,直到现在。截至 2024 年初,浏览器厂商正在逐步将 align-content
实现在流式布局中,以控制内容在“块”方向上的对齐。当前这个新特性仅在 Chrome Canary(需要开启实验标志)和 Safari 技术预览版中可用。
(上面只是个演示,在 Chrome Canary 和 Safari TP 中体验了 align-content
的新支持,然后用 Flexbox 重新创建了相同的效果)
真实项目里可以这样使用吗?
从我观察的情况来看,这个新选项并没有解锁任何新的可能性,至少在我可以创建的 UI 类型方面,已经可以使用本教程中探讨的技术来实现相同的效果。尽管如此,我还是期待它能广泛普及,毕竟感觉有点不必要的是必须切换到完全不同的布局模式才能完成居中对齐的操作。
结语
接触前端这么多年来我曾把 CSS 看作是一系列模式的集合,实际上有很多方法可以用来解决当前遇到的问题。这种方法虽然能奏效,但也感觉有些局限。时不时地一些看似正常的代码会突然出现问题实际运行效果和以前完全不同。花时间深入学习 CSS 时之前老旧的固定方式,而是能够凭直觉灵活的来解决问题!✨
在本文中探讨了一些实用的居中模式,希望它们能在你需要居中对齐时派上用场。其这也只是触及了CSS 中居中对齐的冰山一角!与其死记硬背更多的代码片段,不如建立一个全面的 CSS 理解模型,这样可以即时提出解决方案!
如果你觉得这个教程有用,可以点点赞啦~~