x-height(小写字母x的高度)
内联元素默认是基线对齐的。
ex 是 CSS 中的一个相对单位,指的是小写字母 x 的高度,没错,就是指 x-height
所以如果一个图标高度为1ex,或者元素高1ex、背景center,那么图标和元素就是水平方向对齐的。且不受字体和字号的影响。
内联元素(line-height)
默认空<div>
高度是 0,但是一旦里面写上几个文字,
本质上是由 line-height 属性全权决定的
内容是纯内联元素
对于非替换元素的纯内联元素(比如纯文本),其可视高度完全由 line-height 决定。即content高度。padding和border不会对其有任何影响
内联元素的高度由固定高度和不固定高度组成,这个不固定的部分就是所谓的“行距”。换句话说,line-height 之所以起作用,就是通过改变“行距”来实现的。
在 CSS 中,“行距”分散在当前文字的上方和下方,也就是即使是第一行文字,其上方也是有“行距”的,只不过这个“行距”的高度仅仅是完整“行距”高度的一半,因此,也被称为“半行距”
一般业界的共识是:行距 = 行高− em-box。转换成 CSS 语言就是:行距 = line-height - font-size。
人很容易被肉眼所见的东西迷惑,因此,很多人会把文字图形区域看成是 em-box 范围,实际上这是不正确的,比方说,一些带尾巴的英文字符 q 或者 g,其小尾巴是在 em-box 范围之外的,而对于汉字,很多字体图形高度实际上要小于 em-box 高度的。
大多数场景下,内容区域和 em-box 是不一样的,内容区域高度受 font-family 和font-size 双重影响,而 em-box 仅受 font-size 影响,通常内容区域高度要更高一些。除了下面这种情况,也就是“当我们的字体是宋体的时候,内容区域和 em-box 是等同的”,因为宋体是一种正统的印刷字体,方方正正,所以千万不要小看宋体。
内容是块级或者替换元素
关于替换元素的高度与 line-height 的关系首先需要弄明白这个问题:line-height可以影响替换元素(如图片的高度)吗?答案是,不可以!
可能有人会反驳了,不会呀,你看下面这个例子:
.box {
line-height: 256px;
}
<div class="box">
<img src="1.jpg" height="128">
</div>
由于同属内联元素,因此,会共同形成一个“行框盒子”,line-height 在这个混合元素的“行框盒子”中扮演的角色是决定这个行盒的最小高度,听上去似乎有点儿尴尬,对于纯文本元素,line-height 非常威风,直接决定了最终的高度。但是,如果同时有替换元素,则line-height 的表现一下子弱了很多,只能决定最小高度,对最终的高度表现有望尘莫及之感。为什么会这样呢?一是替换元素的高度不受 line-height 影响,二是 vertical-align属性在背后作祟。
对于这种混合替换元素的场景,line-height 要想一统江山,需要值足够大才行。但是,实际开发的时候,我们给 line-height 设置的值总是很中规中矩,于是,就会出现类似下面的场景:明明文字设置了 line-height 为 20px,但是,如果文字后面有小图标,最后“行框盒子”高度却是 21px 或是 22px。这种现象背后最大的黑手其实是 vertical-align 属性,后续会详细解析此现象。
对于块级元素,line-height 对其本身是没有任何作用的,我们平时改变 line-height,块级元素的高度跟着变化实际上是通过改变块级元素里面内联级别元素占据的高度实现的。因为line-height属性是会自动继承的。
因为 line-height 几乎无处不在的继承特性,并且 CSS 世界是为了更好地图文展示,所以 line-height 不仅是内联元素高度的基石,而且还是整个 CSS 世界高度体系的基石。
line-height属性让文本居中
关于设置line-height让文字居中的方法其实不用设置行高,如果容器高度是auto的话,如果是具体值,那么line-height才会是跟height高度一致。
是否真正居中,其实只是近似居中, 说“近似”是因为文字字形的垂直中线位置普遍要比真正的“行框盒子”的垂直中线位置低
多行文本居中
.box {
line-height: 120px;
background-color: #f0f3f9;
}
.content {
display: inline-block;
line-height: 20px;
margin: 0 20px;
vertical-align: middle;
}
<div class="box">
<div class="content">基于行高实现的...</div>
</div>
原理大致如下:
- 多行文字使用一个标签包裹,然后设置 display 为 inline-block。好处在于既能重置外部的 line-height 为正常的大小,又能保持内联元素特性,从而可以设置vertical-align 属性,以及产生一个非常关键的“行框盒子”。我们需要的其实并不是这个“行框盒子”,而是每个“行框盒子”都会附带的一个产物—“幽灵空白节点”,即一个宽度为0、表现如同普通字符的看不见的“节点”。有了这个“幽灵空白节点”,我们的lineheight:120px 就有了作用的对象,从而相当于在.content 元素前面撑起了一个高度为120px 的宽度为 0 的内联元素。
- 因为内联元素默认都是基线对齐的,所以我们通过对.content 元素设置 verticalalign:middle 来调整多行文本的垂直位置,从而实现我们想要的“垂直居中”效果。如果是要借助 line-height 实现图片垂直居中效果,也是类似的原理和做法。
line-height默认值
line-height 的默认值是 normal,normal 实际上是一个和font-family 有着密切关联的变量值。
不同系统不同浏览器的默认 line-height 都是有差异的。因此,在实际开发的时候,对 line-height 的默认值进行重置是势在必行的。下面问题来了,line-height 应该重置为多大的值呢?是使用数值、百分比值还是长度值呢?数值1.5和数值150%和1.5em有什么区别呢,如果给body设置line-height分别为三者,body的子元素的继承结果分别为:body设置为150%和1.5em时,所有子元素继承的line-height的值为固定的1.5*fontSize,不会根据子元素重新设置的fontSize进行重新计算。body设置为1.5则不同,会根据重新设置的fontSize重新计算。
对于line-height的设置: 如果我们做的是一个重图文内容展示的网页或者网站,如博客、论坛、公众号之类的,那一定要使用数值作为单位,考虑到文章阅读的舒适度,line-height 值可以设置在 1.6~1.8。如果是一个偏重布局结构精致的网站,则在我看来使用长度值或者数值都是可以的,因为,第一,我们的目的是为了兼容;第二,无论使用哪种类型值,都存在需要局部重置的场景。不过,根据我的统计,基本上各大站点都是使用数值作为全局的 line-height值。不过,这并不表示使用数值就一定是最好的,如果网站内容的样式不是动态不可控的,有时候,固定的长度值反而更利于精确布局。因此,不要盲目跟风。
如果使用的是长度值,我建议直接 line-height:20px,排版时候计算很方便。
如果随大流使用的是数值,我建议最好使用方便计算的行高值,一种是 line-height 属性值本身方便计算,另一种是 line-height 的默认计算值方便计算。比方说,1.3、1.4、1.5都有大型网站使用,我们就不妨使用 1.5。 另外一种“默认计算值方便计算”是我们先得到方便计算的 line-height 计算值,然后倒推 line-height 应该使用的数值是多大,例如 20px 是一个非常方便的计算值,如果默认重置的 font-size 是 14px,则 line-height 数值应该是 20px/14px≈1.4285714285714286 四舍五入的结果,于是得到向上舍入取 1.42858
这里还需要注意: HTML 中的很多替换元素,尤其表单类的替换元素,如输入框、按钮之类的,很多具有继承特性的 CSS 属性其自己也有一套,如 font-family、font-size 以及这里的 line-height。由于继承是属于最弱的权重,因此 body 中设置的 line-height 是无法影响到这些替换元素的,所以需要额外使用line-height: inherit;进行兼顾。当然也可以是使用*通配符设置。
line-height的大值特性
例子:
<div class="box">
<span>内容...</span>
</div>
CSS 代码有所不同,分别为
.box {
line-height: 96px;
}
.box span {
line-height: 20px;
}
和
.box {
line-height: 20px;
}
.box span {
line-height: 96px;
}
两者的box高度为多少呢?
正确答案是都是96px!!!
也就是说:无论内联元素 line-height 如何设置,最终父级元素的高度都是由数值大的那个 line-height 决定的,我称之为“内联元素 line-height 的大值特性”。
原因:因为span元素是行内元素,所以前面存在幽灵节点,所以box设置的96px可以生效,对于box设置为20px为什么高度还是96px:因为行框盒子的高度是由高度最高的那个“内联盒子”决定的
上面的span高度为什么不生效呢,原因是span是inline元素,不可设置高度,所以span高度不是对应的20px或者96px,把span设置为inline-block即可解决
vertical-align
凡是 line-height 起作用的地方 vertical-align 也一定起作用,只是很多时候,vertical-align 默默地在背后起作用,你没有感觉到而已。
如下代码:
.box { line-height: 32px; }
.box > span { font-size: 24px; }
<div class="box">
<span>文字</span>
</div>
box高度是多少?很多人一定以为是32px,实则不然,实际会大几px,具体大多少与font-family有关。
这里,之所以最终.box 元素的高度并不等于 line-height,就是因为行高的朋友属性 vertical-align 在背后默默地下了黑手。
属性值
vertical-align 属性值分为以下 4 类:
• 线类,如 baseline(默认值)、top、middle、bottom;
• 文本类,如 text-top、text-bottom;
• 上标下标类,如 sub、super;
• 数值百分比类,如 20px、2em、20%等。
在 CSS 世界中,凡是百分比值,均是需要一个相对计算的值,例如,margin 和 padding是相对于宽度计算的,line-height 是相对于 font-size 计算的,而这里的 verticalalign 属性的百分比值则是相对于 line-height 的计算值计算的
line-height设置值与显示值不同
对字符而言,font-size 越大字符的基线位置越往下,因为文字默认全部都是基线对齐,所以当字号大小不一样的两个文字在一起的时候,彼此就会发生上下位移,如果位移距离足够大,就会超过行高的限制,而导致出现意料之外的高度
img外部容器高度大于img高度
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
line-height: 32px;
}
.box img {
height: 100px;
}
</style>
</head>
<body>
<div class="box">
<img src="./images/cry.jpeg" alt="">
</div>
</body>
</html>
如上代码,box的高度一定是大于100的,原因就是幽灵节点的影响,font-size小于line-height,导致幽灵节点基线下还存在空隙,而img底部又与幽灵节点基线对齐,所以img下会多出一些空隙,导致父盒子高度比img高度高。
解决办法就是避免上述属性的影响。如img设置为块级元素避免幽灵节点,或者line-height足够小,或者设置img的vertical-align
所以当遇到行内元素布局与我们期待的布局相差甚远时,我们要考虑到幽灵节点、line-hight、vertical-align三者的影响。
深入属性值
baseline(默认值)
默认值 baseline 在文本之类的内联元素那里就是字符 x 的下边缘,对于替换元素则是替换元素的下边缘。但是,如果是 inline-block 元素,则规则要复杂了:一个 inline-block 元素,如果里面没有内联元素,或者 overflow 不是 visible,则该元素的基线就是其 margin 底边缘;否则其基线就是元素里面最后一行内联元素的基线。
如下例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.dib-baseline {
display: inline-block;
width: 150px;
height: 150px;
border: 1px solid #cad5eb;
background-color: #f0f3f9;
}
</style>
</head>
<body>
<span class="dib-baseline"></span>
<span class="dib-baseline">x-baseline</span>
</body>
</html>
结果与我们想象应该出现的结果相去甚远,原因就在于上述的规则,第一个元素内部没有内联元素,所以基线就是底部,而第二个元素内部有内联元素,基线就是x-baseline的基线,所以就会出现这样的布局
top/bottom
top:元素底部和当前行框盒子的顶部对齐,bottom即底部对齐。
middle
定义中“基线往上 1/2 x-height 处”,指的就是 middle 的位置,仔细品味一下,“基线”就是字符 x 底边缘,而 x-height 就是字符 x 的高度。考虑到大部分字体的字符 x 上下是等分的,因此,从“基线往上 1/2x-height处”我们就可以看出是字符 x 中心交叉点的位置。换句话说就是,vertial-align:middle可以让内联元素的真正意义上的垂直中心位置和字符 x 的交叉点对齐。
基本上所有的字体中,字符 x 的位置都是偏下一点儿的,font-size 越大偏移越明显,这才导致默认状态下的 vertial-align:middle 实现的都是“近似垂直居中”
text-bottom/text-top
• vertical-align:text-top:盒子的顶部和父级内容区域的顶部对齐。
• vertical-align:text-bottom:盒子的底部和父级内容区域的底部对齐。
内容区域可以看成是 Firefox/IE 浏览器文本选中的背景区域,或者默认状态下的内联文本的背景色区域。而所谓“父级内容区域”指的就是在父级元素当前 font-size 和 font-family 下应有的内容区域大小。
因此,这个定义又可以理解为(以 text-top 举例):假设元素后面有一个和父元素 fontsize、font-family 一模一样的文字内容,则 vertical-align:text-top 表示元素和这个文字的内容区域的上边缘对齐。