CSS十问之元素居中

193 阅读14分钟

大多数人都想要改变世界,却罕有人想改变自己

前言

大家好,我是柒八九。从今天起,我们又重新开辟了一个新的领域:CSS串讲。(毕竟在Web 领域,CSS也是有举足轻重的作用)在该系列文章中,我们会一起学习 元素居中层叠上下文还有一些在面试中比较常见的问题及一些在工作中遇到比较好玩的点。

而今天,我们就从 元素居中:这个让无论是前端"萌新"还是"老油条" 抓耳挠腮的问题。不是因为它难,是因为它太杂了。(这也很契合CSS的学习特定:等)

我记得在看张鑫旭在讲解vertical-align和line-height关系文章 提出来一个观点:

为了快速记住新的东西,而采用的常用方式:
- 情感化认知
- 具象化思维

同样,无论是针对前端Js/Css/Html/Vue/React/Webpack/Vite还是一些新的语言Rust想必都有一些你不熟悉或者模棱两可的知识概念和体系。所以,我们可以尝试用上述的方式,来对新的知识点,进行归纳和梳理。其实,这也算是又重新记忆了一次。只不过,这种方式,可能只属于你一个人能懂的知识关联体系。(那又何妨,我记住了,我骄傲)。

佛陀曾说:我不入地狱,谁入地狱 。这些梳理的事情,就交给我哇。你只负责抽空回来看看(放心,我尽量讲的通俗易懂点)。这也算是,一个工作笔记,平时遇到类似的样式处理,可以快速的通过文章进行检索。于公于私,这个活,我接了。

这里还做一个简短的文章说明:该篇文章,接着居中的话题,一方面讲述比较常规的居中处理方式,然后做一个归纳总结,一方面,把一些css中比较晦涩难懂的知识点,做一个简单介绍。而不是把市面上针对样式居中的所有奇技淫巧都囊括到一起。

话不多说,我们发车了!

简明扼要

  1. 块级元素和displayblock的元素不是一个概念
  2. 对于非替换元素,当left/righttop/bottom,对立方位的属性值同时存在的时候,元素的宽度表现为格式化宽度,其宽度大小相对于最近的具有定位特性(position属性值不是static)的祖先元素计算
  3. margin:auto就是为了填充闲置尺寸而设计的
    1. 如果一侧定值,一侧auto,则auto为剩余空间大小
    2. 如果两侧都是auto,则平分剩余空间
  4. {行高|Line-height}: 指上下文本行基线间的垂直距离
  5. 对于非替换元素的纯内联元素,其可视高度完全line-height决定
  6. 行高实现垂直居中原因在于CSS中行距的上下等分机制

针对居中我们有一个打油诗

-样式居中分两类,水平/垂直惹人怜;
- 每个模式差不离,既定套路得人心;
- 首把元素类型定,行内/块级是旋律;
- 行内水平center一招鲜,垂直padding/line-height/table齐上阵
- 块级首看宽/高是否定,水平常规marigin:auto
- 无论宽/高是否定,子绝父相上绝活
- 无论水平或垂直,遇事不决flex/grid

文章概要

  1. 水平居中
  2. 垂直居中
  3. 水平&垂直居中

知识点简讲

元素分类

CSS世界中,基本上分为两类元素

  • 块级元素
  • 行内元素

常见的块级元素有div/li/table。它们最主要的特点就是:一个水平流上只能单独显示一个块元素

这里有一个点,需要大家厘清:

块级元素和displayblock的元素不是一个概念

例如:

  • <li>元素默认的display值是list-item
  • <table>元素默认的display值是table

其实,如果再往深挖一下的话,有一个结论:

每个元素都有两个盒子
外在盒子: 负责元素是可以一行显示,还是只能换行显示
内在盒子:负责宽高、内容呈现

按照display的属性值不同,

  • block: 外在盒子: 块级盒子;内在盒子:块级容器盒子
  • inline-block:外在盒子:内联盒子;内在盒子:块级容器盒子
  • inline:外在盒子:内联盒子;内在盒子:内联盒子

既然有了前面的针对元素内/外盒子的描述,我们很自然的就联想到。

内联元素:元素的外在盒子具有内联性,具体表现就是 该元素可以和文字在一行显示。

更进一步的讲,我们可以将 displayinline或者inline-*的元素,简单的划分为内联元素。


width:auto

width的默认值是auto

width:auto不同的宽度表现

  1. {充分利用可用空间|Fill-Available}<div>/<p>宽度默认是100%父容器
  2. {收缩与包裹|Shrink-to-Fit}:典型代表有浮动绝对定位inline-blocktable元素
  3. 收缩到最小
  4. 超出容器限制: 具体表现为 内容很长的连续英文和数字或者内联元素被设置white-space:nowrap;

元素尺寸

尺寸分为两类

  1. 内部尺寸:尺寸由内部元素决定
  2. 外部尺寸:尺寸由外部元素决定

外部尺寸的范畴内,针对宽度的又分为两类:

  1. 正常流宽度
  2. 格式化宽度

而外部尺寸的块级元素一旦设置了宽度,流动性就丢失了。

流动性:并不是看上去宽度100%显示那么简单,而是一种margin/border/paddingcontent内容区自动分配水平空间的机制

格式化宽度:默认情况下,绝对定位元素的宽度表现是包裹性宽度由内部尺寸决定

格式化宽度的具体表现为:

对于非替换元素,当left/righttop/bottom,对立方位的属性值同时存在的时候,元素的宽度表现为格式化宽度,其宽度大小相对于最近的具有定位特性(position属性值不是static)的祖先元素计算

有几个重要的点可以简单记住

  1. 非替换元素
  2. position:absolute/fixed
  3. 对立方位同时有值
  4. 相对最近的有定位属性的祖先元素

从侧面说明了,格式化宽度具有流动性。


margin:auto

margin:auto就是为了填充闲置尺寸而设计的

margin:auto用来计算元素对应方向应该获得的剩余间距大小。对应的规则如下:

  1. 如果一侧定值,一侧auto,则auto为剩余空间大小
  2. 如果两侧都是auto,则平分剩余空间

而如何让一个块级元素右对齐margin-left:auto才是最佳实践。margin属性的auto计算就是为块级元素左中右对齐而设计的。


{行高|Line-height}、行距与半行距

  • {上行线高度| ascender height}
  • {大写字母高度| cap height}
  • {基线| baseline}
  • {中线/等分线| median}
  • {下行线高度| descender height}

  • {行高|Line-height}: 指上下文本行基线间的垂直距离,即图中两条红线间垂直距离。

  • 行距: 指一行底线到下一行顶线的垂直距离,即第一行粉线和第二行绿线间的垂直距离。

  • 半行距: 行距的一半,即区域3垂直距离/2,

    • 区域1,2,3,4的距离之和为行高
    • 区域1,2,4距离之和为font-size,所以半行距也可以这么算:(行高-字体size)/2

{行高|Line-height}:内联元素的基石

line-height:是内联元素的高度之本

对于非替换元素的纯内联元素,其可视高度完全line-height决定

内联元素的高度由固定高度不固定高度组成。不固定高度就是行距。换句话说:

line-height就是通过改变行距来改变内联元素高度的

行距 = 行高 - em-boxem-box高度正好就是1em,而em是一个相对font-size大小的CSS单位。即:1em等于当前一个font-size大小。

进而,我们可得出另外一个结论:

行距 = line-height - font-size

line-height比较重要的作用是: 让内联元素垂直居中,而

行高实现垂直居中原因在于CSS中行距的上下等分机制

但是,这种是近似居中:文字字形的垂直中线位置普遍要比真正的行框盒子的垂直中线位置低


1. 水平居中

行内元素-水平居中

针对某个块级元素,然后想让其内联子元素,水平居中。

// 行内元素-水平居中
.center-inline {
  text-align: center;
}

示例比较简单,就不贴具体的html代码了。

固定宽度的块级元素-水平居中

// 固定宽度的块级元素-水平居中
.center-block-fixed-width {
  margin: 0 auto;
}

请注意,在该情况下,是两种情况都需要满足,才可以利用这种方式对元素进行居中处理。

  1. 块级元素
  2. 固定宽度

这两个是&的关系,两者缺一不可。并且,根据前置知识中关于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>

这里有两种比较常规的处理方式。

  1. 将块级元素inline-block
  2. 利用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-start(默认值):左对齐
  • flex-end:右对齐
  • center: 居中
  • space-between:两端对齐,项目之间的间隔都相等。
  • space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。

针对Flex的具体细节,可以参考阮一峰老师写的Flex 布局教程:语法篇,这里也不做延伸。


2. 垂直居中

行内元素-垂直居中

针对行内元素的垂直居中,有分两种情况

  1. 单行垂直居中
  2. 多行垂直居中

单行垂直居中

例如,现在希望某个行内元素的文案在垂直方向居中显示。通过对该元素设置上下方向设置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
  • margin-x: 此时x和top/bottom一致
    在没有设置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%);
}

使用flex

高端的代码,都是朴实无华且简单。

.parent {
  display: flex;
  flex-direction: column;
  justify-content: center;
}

3. 元素水平垂直居中

针对处理这类问题,我们可以通过将 水平居中垂直居中合并起来。可以有(M*N)的解法。但是,在平时工作中,大致可分为四类。

宽&高固定

使用负marigin有很好的兼容性

absolute + 负 margin

.parent {
  position: relative;
}

.child {
  width: 300px;
  height: 100px;
  padding: 20px;

  position: absolute;
  top: 50%;
  left: 50%;

  margin: -70px 0 0 -170px;
}

这里的marigin的计算还是和box-sizing的值强相关。具体细节,可以参考,垂直居中关于这里的解释。

当然,还有一些类似的处理方式也是可以的。例如

absolute + margin auto

利用了,针对margin属性, 如果两侧都是auto,则平分剩余空间

absolute + calc

宽&高不固定

.parent {
  position: relative;
}
.child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

flex布局

.parent {
  display: flex;
  justify-content: center;
  align-items: center;
}

grid 布局

.parent {
  display:grid;
}
.parent .child{
  margin:auto;
}

后记

分享是一种态度,这篇文章,参考很多文章,算是一个自我学习过程中的一种记录和总结。主要是把自己认为重要的点,都罗列出来。同时,也是为大家节省一下排雷和踩坑的时间。当然,可能由于自己认知能力所限,有些点,没能表达很好。

参考资料:

  1. centering-css
  2. flex
  3. 张鑫旭 《CSS进阶》

看都看到这里了,那就劳烦,动动小手手,一键三连哇