垂直对齐:你须要知道的全部事情(翻译)

44年前咱们就把人类送上了月球, 但如今咱们仍然没法在CSS中实现垂直居中。css

————James Anderson 2013/7/20html

注: 由于掘金不支持自定义样式,文中基本全部的图例都没法正常显示,请诸位读者到个人博客上阅读git

前言

原文地址github

Yeah😋, 让咱们来讨论一下vertical-align这个CSS属性。 它常常被用于对齐彼此相邻的文本和元素。就像将图标居中与和它相邻的文本app

可是它有时候看起来像一个SB,由于它全部让人摸不着头脑的规则都在起做用。 举个例子, 可能会发生这样的状况, 你改变一个元素的vertical-align属性以后, 它的对齐方式彻底没有改变。 可是与其相邻的其余元素发生了改变! 这是在逗我呢!ide

因此,为了尽可能减小未来的痛苦,我研究了W3C的CSS specifications, 以便搞清楚vertical-align的行为。布局

你将会在这篇文章中学到什么字体

  1. 内联级(inline-level)元素在线框(Line Box)中的垂直居中行为。
  2. 内联级元素和线框具备基线(Baselines),顶部(Tops)和底部(Bottoms)。
  3. 用vertical-align对齐基线,顶部和底部。
  4. 示例:如何将图标相对与相邻的文本垂直居中。
  5. 示例:基线如何移动。
  6. 示例:如何使垂直居中的元素底部没有空隙。
  7. 示例:如何消除两个对齐的元素之间的空隙。

内联级元素在线框中的垂直居中行为

vertical-align被用于对齐内联级元素 他们的display属性的计算结果为:ui

  • inline
  • inline-block
  • inline-table(未包含在这篇文章中)

Inline elements are basically tags wrapping text.this

内联块级元素如他们的名字通常:存在于行内的块级元素。 他们拥有widthheight(或者由本身的内容决定)以及paddingbordermargin

内联级元素以行的形式排列在一块儿。 当元素过多没法排列在一行以内, 就会在其下方建立一个新的行。 全部的行会有一个所谓的线框(Line Box), 它包含了行的全部内容。

**注:**这里指的是每一行都有一个线框,而不是相邻的行共用一个线框。

线框的高度受其内容的大小影响。 在下面的插图中, 线框的顶部和底部用红色的虚线表示。(原文插图

一行 高的文本。
一行 矮的文本。
这也 有可能 发生。

在这些线框内, vertical-align负责对齐每一个元素。 因此, 什么是元素对齐?

关于基线(Baselines),顶部(Tops)和底部(Bottoms)

垂直对齐最重要的参照点是所涉及元素的基线。 在某些状况下, 元素的封闭盒(enclosing box)的上下边缘也很重要。 让咱们来看看每一个相关类型元素的基线和外边缘的位置。

内联级元素

aAÄ qQ
aAÄ qQ
aAÄ qQ

如今你能看到相邻的三行文本。 红线表示行高的上下边缘, 绿线表示字体的高度, 蓝线表示基线。

  • 最左侧文本的行高与字体大小相等, 所以表明字体高度的绿线和表明行高的红线重叠成了一条线。
  • 中间的文本的行高是字体大小的两倍。
  • 右侧的文本的行高是字体大小的通常。

内联级元素的外边缘与其行高的上下边缘对齐。 行高小于字体的大小可有可无。 因此在上图中, 红线表示的是外边缘。

内联级元素的基线, 字符位于其中。 在图中由蓝线表示。 粗略的讲, 基线位于字体高度中间的某个位置。 详细信息见W3C 规范

inline-block元素

C
C

从左到右你能看到:一个具备内容(一个“C”)的inline-block元素, 一个具备内容且overflow: hiddeninline-block元素, 一个没有内容(可是指定了height)的inline-block元素。 红色表示外边距, 黄色表示边框, 绿色表示内边距, 蓝色表示内容。 在每一个inline-block元素中基线都用蓝线表示。

inline-block元素的外边缘是其外边距顶部和底部的边缘。 在图例中用红线表示。

inline-blick元素的基线取决于元素是否有in-flow内容:

  • 在具备in-flow内容的状况下, inline-block元素的基线是正常流(normal flow)中最后一个内容元素的基线(图例中左侧的例子)。 最后一个元素根据本身的规则找到本身的基线。
  • 在具备in-flow内容且overflow属性的计算结果不是visiable的状况下,基线是外边距的底部边缘(图例中中间的例子)。 因此,它跟inline-block元素的底部边缘相同。
  • 若是不存在in-flow内容, 基线也是外边距的底部边缘(图例中右侧的例子)。

Line Box

This can happen.

你已经在上面看到过这个图例了。 此次我也画出了线框的文本框的上下边缘(绿线)和基线(蓝线)。 还给文本元素加上了灰色的背景以突出其区域。

线框的顶部于最顶部元素的顶部对齐, 线框的底部于最底部元素的底部对齐。 这在上面的图例中以红线所示的方框表示。

线框的基线是可变的:

CSS 2.1 does not define the position of the line box's baseline.

CSS 2.1没有定义线框的基线位置。

— the W3C Specs

vertical-align这个属性上,基线(Baseline)大概是最混乱的部分。 It means, the baseline is placed where ever it needs to be to fulfil all other conditions like vertical-align and minimizing the line box’s height. It is a free parameter in the equation.

由于线框的基线是不可见的,所以它并非显而易见的。 可是咱们能很容易地使它可见。 只须要在行的开头添加一个字符, 好比我在图例中添加了一个“x”。 若是这个字符没有对齐任何地方, 默认状况下会位于基线上。

文本框(Text Box)

咱们将线框基线周围的内容称之为文本框。 文本框能够简单地看做线框内没有任何对齐的inline元素。 它的高度等于父元素的字体大小。 所以, 文本框仅仅包含线框的无格式文本。 该框在上面的图例中用绿线表示。 由于文本框于基线相关联, 当基线移动时文本框也会移动。

注: 这个文本框在W3C规范中被称为strut

啊,这是最难的部分了。 如今咱们知道了一切, 要把事情弄清楚,让咱们快速总结一下最重要的部分:

  • 有一个区域叫作线框(Line Box),这是发生垂直对齐的区域。 他有一条基线(Baseline), 一个文本框(Text Box)和上下边缘。
  • 内联级元素。 它们是对齐的对象, 它们有一条基线和上下边缘。

用vertical-align对齐基线,顶部和底部

By using vertical-align the reference points mentioned in the list above are set into a certain relationship.

相对于线框的基线对齐

x baseline sub super -50% +10px
  • baseline: 元素的基线刚好于线框基线的重叠。
  • sub: 元素的基线位于线框基线下方。
  • <percentage>: 元素的基线相对于线框的基线移动的距离是相对于行高的百分比。
  • <length>: 元素的基线相对于线框的基线移动的距离是一个绝对值

一个特例是vertical-align: middle,以下图例:

x middle
  • middle: 元素上下边缘的中点于线框基线加上x高度的一半对齐。

相对于线框的文本框对齐

x text-top text-bottom

也能够在相对于线框的基线对齐的状况下列出这两个案例, 由于文本框的位置由基线决定(见文本框(Text Box))。

  • text-top: 元素的上边缘于线框中文本框的上边缘对齐。
  • text-bottom: 元素的下边缘于线框中文本框的下边缘对齐。

相对于线框的外边缘对齐

x top bottom
  • top: 元素的上边缘于线框的上边缘对齐。
  • bottom: 元素的下边缘于线框的下边缘对齐。

固然W3C中也有正式的定义

Why Vertical-Align Behaves The Way It Behaves

咱们如今能够在某些状况下仔细地研究垂直对齐了。咱们还会处理一些可能出问题的状况。

居中一个图标

一个困扰个人问题以下:我有一个图标, 我想在一行文本旁边居中放置它。 仅仅给图标设置vertical-align: middle彷佛不是一个使人满意的居中方案。 看看这个示例:

Centered?
Centered!
<!-- left mark-up -->
<span class="icon middle"></span>
Centered?

<!-- right mark-up -->
<span class="icon middle"></span>
<span class="middle">Centered!</span>

<style type="text/css"> .icon { display: inline-block; /* size, color, etc. */ } .middle { vertical-align: middle; } </style>
复制代码

这是一个相同的示例, 可是我画了一些你已经从上文中看到过的辅助线(不知道为何人们能一眼看出这里细微的不协调):

x
Centered?
Centered?

这让咱们的问题有些苗头了。 由于左边的文本彻底没有对齐图标, 它跟基线对齐了。 如今的状况是,经过给图标设置vertical-align: middle咱们将图标的中点和小写字母“x”的中点(即x-height的一半)对齐。因此有升部和降部的字符就会突出。

注: 升部和降部分别指字母向上超出主线和向下超出基线的部分。来自维基百科

注: x-height(译名:X高度)是一个特有名词,是指字母的基本高度,精确地说,就是基线(英语:baseline)和主线之间的距离。特别的,它指称一个字体中小写字母x的高度(这也是这个词的语源)。来自维基百科

在右边, 咱们使整个字体区域对齐垂直方向上的中点。 这时文本的基线向线框的基线的下方移动了一点以对齐中点。 很明显这是一个完美的对齐的方案。

线框基线的移动

使用vertical-align时, 有一个常见的陷阱:基线的位置会被行内全部的元素影响。 让咱们作一个假设, 当一个元素以某种方式对齐时, 线框的基线必定会移动。 大多数垂直对齐(除了顶部和底部对齐)都是相对于基线对齐的, 这会致使线框内的其余元素从新调整其位置。

看看这些示例:

  • 若是一个元素的高度等于整行的高度,vetical-align对这个元素是无效的。 由于没有多余的空间让它移动。 为了相对于线框的基线对齐, 线框的基线就必须移动。 较矮的盒子的vertical-align属性是baseline。 左侧较高的盒子的vertical-align属性是text-bottom。 右侧较低的盒子的vertical-align属性是text-top。 你能看到基线带着较矮盒子移动到了上方。
<!-- left mark-up -->
<span class="tall-box text-bottom"></span>
<span class="short-box"></span>

<!-- right mark-up -->
<span class="tall-box text-top"></span>
<span class="short-box"></span>

<style type="text/css"> .tall-box, .short-box { display: inline-block; /* size, color, etc. */ } .text-bottom { vertical-align: text-bottom; } .text-top { vertical-align: text-top; } </style>
复制代码

就算较高元素的vertical-align属性取其余值, 也会显示如上相同的行为。

  • 即便设置vertical-align的值为bottom(左侧)或top(右侧), 基线也会移动。 这很奇怪, 由于这与基线毫无关系。
<!-- left mark-up -->
<span class="tall-box bottom"></span>
<span class="short-box"></span>

<!-- right mark-up -->
<span class="tall-box top"></span>
<span class="short-box"></span>

<style type="text/css"> .tall-box, .short-box { display: inline-block; /* size, color, etc. */ } .bottom { vertical-align: bottom; } .top { vertical-align: top; } </style>
复制代码
  • 将两个较高元素放在一行内而且将他们垂直居中, 这时基线将会移动以确保两个元素的对齐条件, 同时线框的高度也会调整。 而后加入第三个元素, 这个元素vertical-align属性为middle, 且上下都没有超过线框的边缘。 所以既不会影响线框的高度也不会影响线框基线的位置。 若是它超过了线框的边缘, 那么线框高度和基线又会移动了。 在这种状况下, 前面两个元素被下来了。
<!-- left mark-up -->
<span class="tall-box text-bottom"></span>
<span class="tall-box text-top"></span>

<!-- middle mark-up -->
<span class="tall-box text-bottom"></span>
<span class="tall-box text-top"></span>
<span class="tall-box middle"></span>

<!-- right mark-up -->
<span class="tall-box text-bottom"></span>
<span class="tall-box text-top"></span>
<span class="tall-box text-100up"></span>

<style type="text/css"> .tall-box { display: inline-block; /* size, color, etc. */ } .middle { vertical-align: middle; } .text-top { vertical-align: text-top; } .text-bottom { vertical-align: text-bottom; } .text-100up { vertical-align: 100%; } </style>
复制代码

内联级(inline-level)元素下方可能会有一点小空隙

看看这个示例。 若是你尝试垂直对齐列表中的li元素颇有可能碰到过这种状况。

<ul>
  <li class="box"></li>
  <li class="box"></li>
  <li class="box"></li>
</ul>

<style type="text/css"> .box { display: inline-block; /* size, color, etc. */ } </style>
复制代码

如你所见, 列表项位于基线上。 基线下方留出了一些空间来容纳文本的降部。 这正是咱们看到空隙的缘由。 如何解决这个问题? 既然文本是与基线对齐才留出了空隙,那么咱们只须要将文本与其余的位置对齐就行了。 举个例子, 将列表项设置为vertical-align: middle

<ul>
  <li class="box middle"></li>
  <li class="box middle"></li>
  <li class="box middle"></li>
</ul>

<style type="text/css"> .box { display: inline-block; /* size, color, etc. */ } .middle { vertical-align: middle; } </style>
复制代码

具备内容的inline-block元素不会发送这种状况,由于基线已经位于内容的上方了

内联级元素之间的空隙会破坏布局

这主要是内联级元素自身的问题, 可是由于这是垂直对齐的一个要求,所以了解这一点是很好的。 (不太理解这句话, 下面是原文。)

This is mainly a problem of inline-level elements themselves. But since they are a requirement of vertical-align, it is good to know about this.

你能在第一个示例中的列表项看到空隙。 空隙来自内联元素之间的空白。 内联元素之间的全部空白被合并为一个空格。 若是咱们并排放置两个内联元素而且给它们的width属性赋值50%。那么它们所占据的空间就是两个50%加上一个空格。 显然这超出了一行。 所以该行被分为两行, 这破坏了布局(左侧)。 若是要消除空隙,咱们就须要删除空格,例如使用html注释(右)。

50% wide
50% wide
50% wide
50% wide
<!-- left mark-up -->
<div class="half">50% wide</div>
<div class="half">50% wide... and in next line</div>

<!-- right mark-up -->
   <div class="half">50% wide</div><!-- --><div class="half">50% wide</div>

<style type="text/css"> .half { display: inline-block; width: 50%; } </style>
复制代码

垂直对齐揭秘

yea😋,就是这样。 若是你知道规则,它并不复杂。 若是vertical-align的表现与预期的不一致, 就问问本身下面这些问题:

  • 线框的基线位置以及上下边缘的位置在哪?
  • 内联级元素的基线位置以及上下边缘的位置在哪?

这些问题有可能帮助你解决遇到的难题。

这里有一个更复杂的示例如何垂直居中一个vertical-align属性为middlediv

相关文章
相关标签/搜索