vertical-align:middle近似居中和完美居中

很多人想要垂直居中的时候,第一时间就想到了vertical-align:middle,用完之后,疑惑它怎么会无效呢?

——原来vertical-align 是有适用范围的限制的。

因为水平垂直居中直接就包括了垂直居中,本文直接给出水平垂直居中的例子,并说明其中垂直居中的原理。

本文前面大部分算是理论基础层面列举分析了“近似”和“完美”居中;但是考虑到实际情况,实战还是“一把梭”用的“近似”垂直居中,进而本文最后一章节推导出“普适封装垂直居中”的代码。这样一来,能解释为什么网上其他文章的居中方法看似有差异。

前言

首先需要说明vertical-align只能应用于两个情景:

  • 应用于inline/inline-block元素
  • 应用于table-cell元素(其实就是td元素)

而进一步用vertical-align: middle的效果是:

  • 应用于inline/inline-block元素:元素的中心线和父元素基线上方1/2 x-height处对齐
  • 应用于table-cell元素:简单粗暴理解为像Excel单元格的垂直居中(原文是“使‘单元格的内边距盒模型’在该行内居中对齐”。英文原文的介词断句更加令人费解。)【注意:table-cell这种,本身就可以“完美居中”】

本文就不讲table-cell的例子了,因为它实质上是很简明直观的。

然后我们分类以例子来说明:

大小不固定的图片–水平垂直居中

近似居中

先给容器设置line-height维持一定的高度,然后设置图片vertical-align:middle

<style>
.box {
  line-height: 300px; /*对于只有图片,这里不能用height替代;对于多行文字,若想要完美居中,则要求必须选line-height来配合其他代码,多行文字若想近似居中则用height也可以*/
  text-align: center;
}
.box > img {
  vertical-align: middle;
}
</style>
<body>
  <div class="box">
    <img src="" alt="图片src要设置!!"/>
  </div>
</body>

而实际上这仍不是完美的垂直居中。因为我们知道,通过设置vertical-align: middle;,图片的中线的位置被确定为从行内基线往上1/2 x-height 的位置

(备注:在英文字体设计中,通常以小写字母"x"的高度作为其他小写字母高度的设计的基础。因此x-height被用于表示一款字体中的小写字母"x"的高度。)

(备注:文字处于的位置高度和line-height之间关系的计算非常麻烦,详见我另一篇文章深入理解文字高度和行高的设置。只需记住绝大多数情况下是:文字"x"的中心高度(即基线往上1/2 x-height处) 比line-height的中线高度要低。有些人称这种现象为"字符下沉")

二分之一 x-height

也就是说,假如现在给img后面添加一个"x"作为文字,图片所处的高度不会变,图片的中线位置就穿过文字"x"的中心,这时,图片的中线的位置实际上是比容器行内的中线位置要低的。所以,此乃近似垂直居中。

另外,这种垂直居中方法还要IE8及以上才能兼容。

备注: 如果在IE7里面使用这个方法的时候,HTML结构要调整一下,例如:

<div class="box">
  <img alt="图片src要设置!!"/><!-- 这里要换行或空格 -->
</div>

可以看做是在图片后面创建一个空白的文本节点。


如果我们进而想要完美居中,可以有两种办法:

  • font-size设为0
  • 添加辅助元素

完美居中–font-size设为0

完美居中:在"近似居中"的基础上,我们给.box加多一个font-size:0 即可。

.box {
  /* 添加: */
  font-size: 0;
}

如果读过深入理解文字高度和行高的设置 这篇文章的原理分析,我们会发现,如果设置字号为0,基线、x-height线等等线都合为一体(因为它们之间的距离为0嘛)。所以1/2 x-height 的位置也变成在行内最中间的那里,图片就完美居中了。

完美居中–添加辅助元素

完美居中:在"近似居中"的基础上,添加一个辅助元素使得其变成完美居中:

首先HTML结构中插入一个 i 元素;

然后CSS步骤有三:

  1. 辅助元素inline-block化
  2. 辅助元素宽度为0,高度为100%
  3. vertical-align:middle
  • 注:对于父容器line-height: 300px :对于只有图片,这里不能用height替代;对于多行文字,若想要完美居中,则要求必须选line-height来配合其他代码,多行文字若想近似居中则用height也可以
    • (如果选择用height,对于只有图片,就会“把垂直居中丢失掉”,可以跑demo试试)
    • (如果选择用height,且图片还有其他兄弟元素,可能图片能受影响以达到近似垂直居中)
  • 总之,这里若想要“完美居中”,还要再加上一个条件:使用line-height(而不是别的)来控制父容器高度。
<style>
  /* 添加: */
  i {
    display: inline-block;
    height: 100%;
    vertical-align: middle;
  }
</style>
<body>
  <div class="box">
    <img src="" alt="图片src要设置!!"/>
    <i></i>
  </div>
</body>

备注: 这个辅助元素也可以是伪元素,替代 i 元素即可:

.box:before {
    content:'';
    width: 0;

    display:inline-block;
    vertical-align:middle;
    height:100%;
}

多行文字–水平垂直居中

  • 我们把多行文字统一用display:inline-block的span来包裹,该span就是相当于上文的img元素的地位,则对该span做一样的垂直居中操作。
  • (一般来说,对于多行文字,需要重置外部继承的line-height属性(和text-align属性))

这种方法同样还要IE8及以上才能兼容。

近似居中

<style>
.box {
  line-height: 300px; /* 前文已解释过。若想“完美”垂直居中,必要条件就是选line-height */
  text-align: center;
}
.box > .text {
  display: inline-block;
  vertical-align: middle;
  
  line-height: normal;/* 重置继承 */
  text-align: left;/* 重置继承 */
  max-width: 100%; /* 优化样式 */
}
</style>
<body>
<p class="box">
<span class="text">一行文字<br>一行文字<br>一行文字</span><!-- 兼容IE7,这里要换行或空格 -->
</p>
</body>

如果我们进而想要完美居中,原理同上文的图片,可以有两种办法:

  • 父元素font-size设为0
  • 添加辅助元素

完美居中–font-size设为0(bad)

给父元素设置font-size为0后,由于是文本,所有子元素会继承父元素的font-size为0,所以又要再重置这多行文字的font-size回正常。相对而言麻烦,所以对于文字来说,不推荐这种方法。【这里也略去示例代码,就是父容器添加font-size:0即可】

完美居中–添加辅助元素(good)

原理已经在上文解释过了:

<style>
/* 添加: */
i {
  display: inline-block;
  height: 100%;
  vertical-align: middle;
}
</style>
<body>
<p class="box">
<span class="text">一行文字<br>一行文字<br>一行文字</span>
<i></i>
</p>
</body>

普适封装垂直居中:近似垂直居中

  • 【推导过程】看完前面我们发现,
    • 实际开发中,垂直居中如果能达到近似居中的效果就可以了
    • 完美居中要求之一是必须使用line-height而不是height,这点要求就是不普适、不快捷
      • 解释:如果选择用line-height,影响子元素样式大,导致“居中图片”和“居中多行文字”代码不统一难封装,又不方便记忆
      • 解释:完美居中只限line-height。用了line-height若想要再实现其他样式也不灵活。
    • 所以我们在实战中,直接使用近似居中来封装即可,这样可以方便统一普适封装“居中图片”和“居中多行文字”
      • 再加上简单记忆一些其他的样式实现手段,就能快捷完成开发。

  • (解释适用对象和对应代码:)
    • 当欲设置verticial-align:middle时,img可视为一个低配版的inline-block元素。
    • 当欲设置verticial-align:middle时,“居中单行文字”其实可以和“居中多行文字”使用相同的手段。
  • 普适封装: “居中图片”和“居中多行文字”(包括“居中单行文字”)皆可用它
    • 效果已确定为近似垂直居中【原因上面已分析】
    • 选择使用“添加辅助元素”方案 (仅因为比font-size方案更普适)
    • 效果:这样,父容器可以选择使用line-height或者height来撑高,放宽了适用场景
      • (具体来讲:)
        • 遇到最坏的情况就是对于只有图片,父容器必须选择使用line-height,不然连“居中”效果都达不到。解决办法就是增加“居中的兄弟元素”来打破“只有图片”的场景;
        • 或者 图片 受兄弟元素(含父元素的伪元素)的vertical-align:middle影响而已经达到近似居中,则可不强制使用line-height
  • 其他代码:
    • (然后水平居中代码简单,另外再加代码就行了)
    • 如果应用完这套样式后仍然没有达到居中,则简单粗暴给所有子元素都加上vertical-align:middle(甚至给父元素也加上)
      • (解释:这样减少许多记忆的负担,通常即便父元素加上了vertical-align:middle也没啥损失)
      • (对比:display:table-cell的方案中, vertical-align:middle 是作用于父元素,所以粗暴都加上,省去记忆麻烦)
  • (备注:前文说过,这个辅助元素可以是伪元素,也可以是 i 元素。)
  • (备注:以上,能解释为什么其他人的文章的居中方法有差异)
<style>
.vertical-center:before {
    content:'';
    width: 0; /* 如果为了应对各种场景而普适省事,可以加上 width: 0 */

    display:inline-block;
    vertical-align:middle;
    height:100%;
}
.vertical-center > img {
  vertical-align: middle;
}
.vertical-center > .vertical-center__text {
  display: inline-block;
  vertical-align: middle;
}
/* 灵活:在此垂直居中基础上,加上水平居中 */
.vertical-center { text-align: center; }
/* 【测试代码,可删】: *//* 效果是:放宽了适用场景 */
.vertical-center { height: 300px }
</style>
<body>
  <div class="vertical-center">
    <img src="" alt="图片src要设置!!"/>
    <span class="vertical-center__text">一行文字AAA<br>一行文字<br>一行文字</span>
    <span class="vertical-center__text">单行文字</span>
    <!-- 如上,单行文字,如果想垂直居中,应放入span元素里面,然后和“多行文字”的样式处理一致:一致span套上对应的class -->
  </div>
</body>
文章目录
  1. 前言
  2. 大小不固定的图片–水平垂直居中
    1. 近似居中
    2. 完美居中–font-size设为0
    3. 完美居中–添加辅助元素
  3. 多行文字–水平垂直居中
    1. 近似居中
    2. 完美居中–font-size设为0(bad)
    3. 完美居中–添加辅助元素(good)
  4. 普适封装垂直居中:近似垂直居中