大家好,我是柒八九
。从今天起,我们又重新开辟了一个新的领域:CSS
串讲。(毕竟在Web 领域,CSS
也是有举足轻重的作用)在该系列文章中,我们会一起学习 「元素居中」、「层叠上下文」还有一些在面试中比较常见的问题及一些在工作中遇到比较好玩的点。
而今天,我们就从 「元素居中」:这个让无论是前端"萌新"还是"老油条" 抓耳挠腮的问题。不是因为它难,是因为它太杂了。(这也很契合CSS
的学习特定:「杂」、「多」、「乱」等)
我记得在看张鑫旭在讲解vertical-align和line-height关系文章 提出来一个观点:
❝为了快速记住新的东西,而采用的常用方式: - 「情感化认知」 - 「具象化思维」 ❞
同样,无论是针对前端Js/Css/Html/Vue/React/Webpack/Vite
还是一些新的语言Rust
想必都有一些你不熟悉或者模棱两可的知识概念和体系。所以,我们可以尝试用上述的方式,来对新的知识点,进行归纳和梳理。其实,这也算是又重新记忆了一次。只不过,这种方式,可能只属于你一个人能懂的知识关联体系。(那又何妨,我记住了,我「骄傲」)。
佛陀曾说:「我不入地狱,谁入地狱」 。这些梳理的事情,就交给我哇。你只负责抽空回来看看(放心,我尽量讲的通俗易懂点)。这也算是,一个工作笔记,平时遇到类似的样式处理,可以快速的通过文章进行检索。于公于私,这个活,我接了。
这里还做一个简短的文章说明:该篇文章,接着「居中」的话题,一方面讲述比较常规的居中处理方式,然后做一个归纳总结,一方面,把一些css
中比较晦涩难懂的知识点,做一个简单介绍。而不是把市面上针对样式居中的所有「奇技淫巧」都囊括到一起。
display
为block
的元素不是一个概念left/right
或top/bottom
,「对立方位」的属性值「同时存在」的时候,元素的宽度表现为「格式化宽度」,其宽度大小相对于最近的具有「定位特性」(position
属性值「不是」static
)的祖先元素计算margin:auto
就是为了「填充闲置尺寸」而设计的
1. 如果一侧定值,一侧auto,则auto为「剩余空间」大小
2. 如果两侧都是auto,则「平分」剩余空间line-height
决定针对居中我们有一个「打油诗」
❝-样式居中分「两类」,水平/垂直惹人怜; - 每个模式差不离,既定套路得人心; - 首把元素「类型」定,「行内/块级」是旋律; - 行内水平
center
一招鲜,垂直padding/line-height/table
齐上阵 - 块级首看宽/高是否定,水平常规marigin:auto
; - 无论宽/高是否定,「子绝父相」上绝活 - 无论水平或垂直,遇事不决「flex/grid」 ❞
在CSS
世界中,基本上分为「两类」元素
常见的块级元素有div/li/table
。它们最主要的特点就是:「一个水平流上只能单独显示一个块元素」。
这里有一个点,需要大家厘清:
❝块级元素和
display
为block
的元素不是一个概念 ❞
例如:
<li>
元素默认的display
值是list-item
<table>
元素默认的display
值是table
其实,如果再往深挖一下的话,有一个结论:
❝每个元素都有「两个盒子」 「外在盒子」:负责元素是可以一行显示,还是只能换行显示 「内在盒子」:负责宽高、内容呈现 ❞
按照display的属性值不同,
block
: 外在盒子: 块级盒子;内在盒子:块级容器盒子inline-block
:外在盒子:内联盒子;内在盒子:块级容器盒子inline
:外在盒子:内联盒子;内在盒子:内联盒子既然有了前面的针对元素内/外盒子的描述,我们很自然的就联想到。
内联元素:「元素的外在盒子具有内联性」,具体表现就是 该元素可以和文字在一行显示。
更进一步的讲,我们可以将 display
为inline
或者inline-*
的元素,简单的划分为内联元素。
❝
width
的默认值是auto
❞
width:auto不同的宽度表现
<div>/<p>
宽度默认是100%
于「父容器」white-space:nowrap;
尺寸分为两类
在外部尺寸的范畴内,针对宽度的又分为两类:
而外部尺寸的块级元素一旦「设置了宽度」,流动性就丢失了。
❝「流动性」:并不是看上去宽度100%显示那么简单,而是一种
margin
/border
/padding
和content
内容区「自动分配水平空间」的机制 ❞
「格式化宽度」:默认情况下,「绝对定位」元素的宽度表现是包裹性,「宽度由内部尺寸决定」。
而「格式化宽度」的具体表现为:
❝对于「非替换」元素,当
left/right
或top/bottom
,「对立方位」的属性值「同时存在」的时候,元素的宽度表现为「格式化宽度」,其宽度大小相对于最近的具有「定位特性」(position
属性值「不是」static
)的祖先元素计算 ❞
有几个重要的点可以简单记住
position:absolute/fixed
从侧面说明了,「格式化宽度」具有流动性。
❝
margin:auto
就是为了「填充闲置尺寸」而设计的 ❞
margin:auto
用来计算元素对应方向应该获得的「剩余间距」大小。对应的规则如下:
而如何让一个块级元素「右对齐」,margin-left:auto
才是最佳实践。margin
属性的auto
计算就是为「块级元素左中右对齐」而设计的。
font-size
,所以半行距也可以这么算:(行高-字体size)/2line-height:是「内联元素」的高度之本
❝对于「非替换」元素的「纯内联元素」,其可视高度「完全」由
line-height
决定 ❞
内联元素的高度由「固定高度」和「不固定高度」组成。不固定高度就是「行距」。换句话说:
❝
line-height
就是通过「改变行距」来改变内联元素高度的 ❞
行距 = 行高 - em-box
,em-box
高度正好就是1em
,而em
是一个相对font-size
大小的「CSS单位」。即:1em
等于当前一个font-size
大小。
进而,我们可得出另外一个结论:
❝行距 =
line-height
-font-size
❞
而line-height
比较重要的作用是:「让内联元素垂直居中」,而
❝行高实现垂直居中原因在于CSS中「行距的上下等分机制」 ❞
但是,这种是「近似」居中:文字字形的垂直中线位置普遍要比真正的行框盒子的垂直中线位置低
针对某个块级父元素,然后想让其内联子元素,水平居中。
// 行内元素-水平居中
.center-inline {
text-align: center;
}
示例比较简单,就不贴具体的html
代码了。
// 固定宽度的块级元素-水平居中
.center-block-fixed-width {
margin: 0 auto;
}
请注意,在该情况下,是两种情况都需要满足,才可以利用这种方式对元素进行居中处理。
这两个是&
的关系,两者缺一不可。并且,根据前置知识中关于margin:auto
的介绍。很自然就会想明白为何通过maring:0 auto
就可以将定宽的块级元素水平居中了。
我们继续来解释下,首先,块级元素定宽,也就是说该元素「流动性」消失了,不会100%于父级元素的宽度了。换言之,就是该元素在水平方向无法将父元素填充满。既然,存在了「闲置」空间,那么,margin:auto
就是干这个事的,所以他们两个一拍即合。
我们将这个例子世俗化一下:将块级元素,想象成某个当红小生。在神秘力量的驱使下,当红小生原来「左右逢源」(占满一行)的粉丝市场被束缚了(定宽了),没法拥有原来的荣光了,又想占据C
位,自身实力不够,那就需要贵人扶持。在margin:auto
这个贵人的大力支持下,这位小生又站起来了。(居中了)
如果有一个需求,需要将「多个块级」元素水平居中。
// xx 会被后续的特定的类名替换
<main class="xx-center">
<div>
块1
</div>
<div>
块2
</div>
<div>
块3
</div>
</main>
这里有两种比较常规的处理方式。
inline-block
化flexbox
inline-block
// 父元素 设置水平居中
.inline-block-center {
text-align: center;
}
// 块级元素 `inline-block`化
.inline-block-center div {
display: inline-block;
text-align: left;
}
具体原理,其实和针对内行元素水平居中是一个道理。这里不做过多解释。
flexbox
.flex-center {
display: flex;
justify-content: center;
}
父级元素设置display:flex
,成为 Flex 容器Flex Container,简称"容器"。它的所有「子元素」自动成为容器成员,称为 Flex 项目Flex Item,简称"项目"。
同时,在容器上设置justify-content
,该属性定义了项目在「主轴」上的对齐方式。
.box {
justify-content: flex-start | flex-end | center | space-between | space-around;
}
它可能取5个值,具体对齐方式与轴的方向有关
针对Flex的具体细节,可以参考阮一峰老师写的Flex 布局教程:语法篇,这里也不做延伸。
针对行内元素的垂直居中,有分两种情况
例如,现在希望某个行内元素的文案在垂直方向居中显示。通过对该元素设置「上下」方向设置pading
。以到达将文本信息,放置到中间位置的效果。
.center-text-vertical {
padding-top: 30px;
padding-bottom: 30px;
}
如果,在有些情况下,padding
属性无法生效(反正就是不让用),并且当前文本信息不会换行,即white-space:nowrap
。此时,就需要另外一种「邪门外道」: 设置line-height
.center-text-vertical-trick {
line-height: 100px;
white-space: nowrap;
}
针对多行元素在垂直方向的居中,通过在利用table
元素的td
的「默认」属性:即在垂直方向上vertical-align: middle;
<table>
<tr>
<td>
我是一个多行文本信息 bala bala
</td>
</tr>
</table>
这种其实是直接利用了table
元素的默认属性,实现想要的效果。
有时候,我们不想用table
进行页面布局。所以,我们就利用display:table
手动将某些元素指定为拥有table
布局属性的元素。
<div class="center-table">
<p>我是一个多行文本信息 bala bala </p>
</div>
由于行文所限,只写出特定的布局样式。
.center-table {
display: table;
}
.center-table p {
display: table-cell;
// 手动指定 垂直方向居中显示
vertical-align: middle;
}
如果还嫌弃麻烦的话,我们还有另外一种方式。只需要在父级元素中设置特定的属性,对应的子元素就会在垂直方向上居中显示。
那就是flex
布局。
<div class="flex-center">
<p>我是一个多行文本信息 bala bala</p>
</div>
在父级元素,一劳永逸的设置子元素居中样式
.flex-center {
display: flex;
flex-direction: column;
justify-content: center;
height:200px; //这里不能缺少
}
「Note」: 如果想让居中效果有效,有一个前提条件就是,父级元素必须有一个定高(px,%)。
如果,高度无法定死,那就需要另辟蹊径了。利用一个伪类::before
采用「幽灵节点」来实现。
<div class="ghost-center">
<p>我是一个多行文本信息 bala bala</p>
</div>
对应的样式代码如下:
.ghost-center {
position: relative;
}
.ghost-center::before {
content: " ";
display: inline-block;
// 画龙点睛之笔
height: 100%;
width: 1%;
vertical-align: middle;
}
.ghost-center p {
display: inline-block;
vertical-align: middle;
}
.parent {
position: relative;
}
.child {
position: absolute;
top: 50%;
height: 100px;
margin-top: -70px;
padding:20px;
}
这里有几点,需要注意
position:relative
position:absolute
box-sizing: border-box
情况下,需要 height/2 + padding-x+ border-x
在元素高度确定的情况下,我们可以通过height/2 + padding-x+ border-x
等公式计算出,需要在垂直方向移动的距离。但是,针对元素高度不定的情况,我们就需要想其他的办法了。
.parent {
position: relative;
}
.child {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
高端的代码,都是朴实无华且简单。
.parent {
display: flex;
flex-direction: column;
justify-content: center;
}
针对处理这类问题,我们可以通过将 「水平居中」和「垂直居中」合并起来。可以有(M*N)的解法。但是,在平时工作中,大致可分为四类。
使用负marigin有很好的「兼容性」。
.parent {
position: relative;
}
.child {
width: 300px;
height: 100px;
padding: 20px;
position: absolute;
top: 50%;
left: 50%;
margin: -70px 0 0 -170px;
}
这里的marigin
的计算还是和box-sizing
的值强相关。具体细节,可以参考,垂直居中关于这里的解释。
当然,还有一些类似的处理方式也是可以的。例如
利用了,针对margin
属性, 如果两侧都是auto,则「平分」剩余空间
.parent {
position: relative;
}
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.parent {
display: flex;
justify-content: center;
align-items: center;
}
.parent {
display:grid;
}
.parent .child{
margin:auto;
}
「分享是一种态度」,这篇文章,参考很多文章,算是一个自我学习过程中的一种记录和总结。主要是把自己认为重要的点,都罗列出来。同时,也是为大家节省一下「排雷和踩坑的时间」。当然,可能由于自己认知能力所限,有些点,没能表达很好。
参考资料: