浅谈line-height和vertical-align

本文原创:liruifang

作为一个前端人,经常混迹于图片和文字相互糅杂的江湖中,稍有不慎便会中招,本文主要从line-height和vertical-align入手,分析项目中出现的“诡异”现象,透过现象看本质,从而化敌为友为我所用。

目录

  • 问题现象
  • 属性介绍
  • 问题剖析
  • 简单应用
  • 参考文献

一、令人抓狂的现象

1. 单行文本占据的高度
<style type="text/css">
    .demo1 {
        line-height: 100px;
        background-color: pink;
    }
    .demo1 span {
        font-size: 40px;
        background-color: grey;
    }
</style>
<div class="demo1">
    <span>xxJD-BG-FExx</span>
</div>
demo1.png

为什么单行文本的可视高度是109px,而不是100px?

2. 图片下面的空隙
<style type="text/css">
    .demo2 {
        border: 1px solid black;
        width: 1080px;
    }
    .img {
        display: inline-block;
        height: 296px;
    }
    .text {
        background: pink;  
    }
</style>
<div class="demo2">
    <img  class="img" src="img/konan.jpg">
</div>
img-gap.png

图片下方为什么会出现空隙?

3. 图文对齐问题
<style type="text/css">
    .demo3 {
        font-size: 40px;
    }
    .icon {
        width: 1em;
        vertical-align: middle;
    }
    .gap {
        display: inline-block;
    }
    .gap span {
        /*vertical-align: middle;*/
    }
</style>
<div class="demo3">
    <p class="gap"><img class="icon" src="img/unpaid.png">待支付:</p>
</div>
img-text-1.png

明明用了vertical-align: middle,图片位置为什么偏下?

4. 矩形块对齐异常
<style type="text/css">
    .demo4 {
        display: inline-block;
        width: 100px;
        height: 100px;
        border: 1px solid red;
    }
    .align {
        /*line-height: 0;*/
        /*font-size: 0;*/
    /*    vertical-align: top;*/
    }
</style>
<div class="demo4"></div>
<div class="demo4"></div>
<div class="demo4">x见证奇迹x</div>
juxing.png

矩形块为什么不是一行排列?

5.vertical-align怪异反应
<style type="text/css">
    .ctn-block{
        background-color: #bbb;
    }
    .ctn-block .child {
        display: inline-block;
        width: 100px;
        background-color: aliceblue;
    }
    .ctn-block .child-1 {
        height: 100px;
        vertical-align: middle;
    }
    .ctn-block .child-2 {
        height: 200px;
    }
    .ctn-block .child-3 {
        height: 300px;
    }
</style>
<div class="ctn-block">
    <div class="child child-1"></div>
    <div class="child child-2"></div>
    <div class="child child-3"></div>
</div>
noMiddle.png

最小矩形块明明设置了vertical-align: middle;为什么没有居中对齐,反而感觉“掉”下去了?

二、透过现象看本质

(一)line-height

  • 本页内容来自W3C:

http://www.w3school.com.cn/cssref/pr_dim_line-height.asp

  • 定义和用法

line-height 属性设置行间的距离(行高)

  • 注释

不允许使用负值

  • 说明

a.该属性会影响行框的布局。在应用到一个块级元素时,它定义了该元素中基线之间的最小距离而不是最大距离。
b.line-height 与 font-size 的计算值之差(在 CSS 中成为“行间距”)分为两半,分别加到一个文本行内容的顶部和底部。可以包含这些内容的最小框就是行框。

  • 取值
取值 描述
normal 默认。设置合理的行间距。
number 设置数字,此数字会与当前的字体尺寸相乘来设置行间距。
length 设置固定的行间距。
% 基于当前字体尺寸的百分比行间距。
inherit 规定应该从父元素继承 line-height 属性的值。
  • 划重点

a.分成两半加到文本行上下
b.相对值:相对于font-size计算

font-size: 14px;
line-height: 1.5;(21px)
line-height: 150%;
line-height: 1.5em;

都是21px,三者是否完全一样?

<head>
    <title>line-height相对值</title>
    <style type="text/css">
        body,h3,p {
            margin: 0;
        }
        body {
            font-size: 14px;
            line-height: 1.5;
            /*line-height: 150%;*/
            /*line-height: 1.5em;*/
        }
        h3 {
            font-size: 32px;
        }
        p {
            font-size: 20px;
        }
    </style>
</head>
<body>
    <h3>标题</h3>
    <p>内容</p>
</body>

上例中,vertical-align:1.5时,标题和文字显示正常;其他两个值时,标题和文字却挤在了一起。

分析:继承的差别,line-height为数值时,所有子元素继承的都是这个值;而另外两个相对值,子元素继承的是最终的计算值。

line-height.png
1. 对于块级元素,line-height指定了元素内部行框盒子(line-boxes)的最小高度,对块级元素本身没有任何作用
2. 对于非替代纯内联元素,可视高度完全由line-height决定(对,是完全),line-height指定了用来计算行框盒子高度的基础高度
3. 对于替代行内元素,例如img、input、button,line-height没有影响

例子1

<div class="demo">
    <img src="img/konan.jpg">
</div>
.demo {
    line-height: 256px;
}
.demo img {
    height:128px;
}

分析:根据以上规则,line-height对块级元素本身没有任何作用,指定的是内部行框盒子的最小高度,而本例中内部是替代行内元素,line-height对其没有影响,那么,最外层div的可视高度是多少?128px!是256px

strut(直译:支撑物)

  • 存在的前提:必须是html5声明

  • 规范中的定义: Each line box starts with a zero-width inline box with the element's font and line-height properties. We call that imaginary box a ”struct"。

  • 解释:每一个行框盒子开始的位置都有一个与之行高、字体大小相同,宽度为零的内联盒子。===> 行框前面有一个宽为0的空字符 ===>幽灵空白节点(张鑫旭大师命名)

例子2

<div class="demo">
    <span>此处是一行文字</span>
</div>
case1
    .demo {
        line-height: 96px;
    }
    .demo span {
        line-height: 20px;
    }

case2
    .demo {
        line-height: 20px;
    }
    .demo span {
        line-height: 96px;
    }

以上两种情况下,最外层div的可视高度是多少?都是96px

总结
  • 行框盒子的高度是由高度最高的那个内联盒子决定的
  • 注意”幽灵空白节点“

那么问题来了,如果是图文混杂的混合的行框盒子,高度如何表现?

只能决定最小高度,最终高度说了不算

why?

1.替代行内元素不受控制
2.vertical-align的影响

(二)vertical-align

  • 本页内容来自W3C

http://www.w3school.com.cn/cssref/pr_pos_vertical-align.asp

  • 定义和用法

vertical-align 属性设置元素的垂直对齐方式。

  • 说明

1.该属性定义行内元素的基线相对于该元素所在行的基线的垂直对齐。
2.允许指定负长度值和百分比值。这会使元素降低而不是升高。
3.在表单元格中,这个属性会设置单元格框中的单元格内容的对齐方式。

  • 取值
取值 描述
baseline 默认。元素放置在父元素的基线上
sub 垂直对齐文本的下标
super 垂直对齐文本的上标
top 把元素的顶端与行中最高元素的顶端对齐
text-top 把元素的顶端与父元素字体的顶端对齐
middle 把此元素放置在父元素的中部
bottom 把元素的顶端与行中最低的元素的顶端对齐
text-bottom 把元素的底端与父元素字体的底端对齐
length
% 使用 line-height的百分比值排列,允许负值
inherit 规定应该从父元素继承 vertical-align 属性的值
  • 划重点

a. 作用前提:只应用于内联元素以及display: table-cell的元素;比如float和position:absolution会使元素块级化,没有理由和vertical-align同时出现
b.相对值:相对于line-height计算
c.基线为何物?
d.父元素的中部指的是?

问题一:基线
  • 字母x的下边缘就是我们的基线:
baseline.png
  • 小写字母'x'衍生出了"x-height"概念,指的的是小写字母x的高度,在css中用ex来表示这个相对单位

  • baseline确定规则:

1.inline-table元素:table第一行的baseline
2.父元素line box:最后一个inline box 的baseline
3.纯文本的内联元素:字符x的下边缘
4.替换元素:替换元素的下边缘
5.inline-block:
(1) 内部没有内联元素,或者overflow不为visible,则:baseline是该元素margin底边缘
(2) 内部有内联元素,baseline是元素里面最后一行内联元素的基线

问题二:父元素的中部
  • vertical-align: middle; 把此元素放置在父元素的中部

内联元素:元素的垂直中心点和行框盒子基线往上1/2 x-height处对齐,即字符x的中心交叉位置点。
table-cell:单元格盒子相对于外面的表格行居中对齐。

middle.png

三、真相只有一个(解决最初的问题)

  • 嫌疑人:line-height,vertical-align,幽灵空白节点

  • 突破口:
    line-box的基线,在哪里?
    inline级元素的基线,在哪里?
    line box前后打个小写字母“x”瞧瞧

现象一:单行文本占据的高度

demo1.png
  • 分析:对于字符而言,font-size越大,基线位置越往下,因为文字默认全部是基线对齐,字号不一致的文字在一起,会发现上下位移。
myFont.png
  • 解决办法
    “幽灵空白节点”的字体和后面span的字体一样大
    改变垂直对齐方式,如:vertical-align:top等

现象二:图片间隙问题(inline元素的水平垂直空隙,常见:img、li等)

  • 水平空隙是因为换行引起的,这个换行会变成一个空白,这个空白会被解析为DOM中的文本节点
  • 垂直空隙:三大元凶
img-gap.png
  • 分析

vertical-align默认值是baseline, 也就是基线对齐。
“幽灵空白节点”的高度是由行高决定的

  • 解决办法

通过display或者position,float等将inline水平的img变成block,一口气干掉三个元凶
使用其他vertical-align值
直接修改line-height值:下面的空隙高度,实际上是文字计算后的行高值和字母x下边缘的距离。因此,只要行高足够小,实际文字占据的高度的底部就会在x的上面,下面没有了高度区域支撑
line-height为相对单位,font-size间接控制

现象三:图文对齐表现

img-text.jpg
  • 分析

没有经过处理的文字,其基线和line box的基线重合,图片设置vertical-align:middle,其正中心对准父级元素的x的正中心;
但是x字母的正中心并不是其所在的text-box的正中心,而是text-box中心稍稍的偏下了一点

  • 解决办法

我们把“center”包裹起来并为也设置 vertical-align: middle,“center”的基线就不再和line-box的基线重合了,而是稍稍下移了,图文对齐
利用margin-bottom处理
利用vertical-align: length(负值), 设置数值进行对齐
尺寸合适的情况下,可以采用1ex的方式(经验取值基于20px的图标)

现象四:矩形块对齐异常

juxing.png
  • 分析

根据基线的确定规则,框里没有内联元素的,基线是容器的margin下边缘,框内有文字的,基线是文字的基线:字母x的下边缘;因此:左框下边缘与右框x字符底边对齐

  • 解决办法

line-height:0;或者font-size:0;(间隔更大,why?提示:line-height为0,字符占据的位置也为0,高度的起始位置变成字符内容框的垂直中心位置[存在字符下沉],字符上移,基线也上移)
换一种对齐方式,vertical-align设为其他值

现象五:vertical-align:middle让元素下移而不居中

noMiddle.png
  • 分析

middle对齐的参照标准是将子元素盒子的垂直中点与父盒子的baseline加上父盒子的x-height的一半位置对齐

  • 解决办法

一种方式是将最高的元素设为vertical-align:middle,然后将想要居中的也设定为vertical-align:middle
(解释)最高元素设定为vertical-align:middle后,这个元素对于line box来说,baseline就是其中线,再将需要设定垂直居中的元素也设定为vertical-align:middle,它们的baseline必然在最高元素的baseline之上,所以被强制下移,进行居中。

四、化敌为友(简单的应用)

(一)不受字体字号影响的内联元素的垂直居中对齐效果:

内联元素默认是基线对齐的,而基线就是x的底部,而1ex就是一个x的高度。设想下,假如我们图标高度就是1ex, 同时背景图片居中,岂不是图标和文字天然垂直居中,而且,完全不受字体和字号的影响。因为ex就是一个相对于字体字号的单位。

<div>
    liruifang<i class="icon-arrow"></i>
    京东前端<i class="icon-arrow"></i>
</div>
<style type="text/css">
    .icon-arrow {
        display: inline-block;
        width: 20px;
        height: 1ex;
        background: url(img/arrow.png) no-repeat center;
    }
</style>

(二)垂直居中

  • 单行文字:line-height设置为需要的高度即可,不需要设置height,如果设置则两者需要保持一样。
  • 多行文字:高度固定,文字单行或者多行,字体有大有小,如何居中?(提示:利用幽灵空白节点,与图片居中类似)
  • 图片:利用幽灵空白节点响应line-height形成高度,此时,图片再来个vertical-align:middle,就可以和这个被行高撑高的幽灵空白节点(近似)垂直对齐了。
  • 近似的原因:字符x的位置都是偏下一点的


    middle-1.png
  • 完全居中:font-size:0,绝对中心线和中线重合。

(三)利用line-block和基线多变之间的关系:处理图文对齐

  • 例如,实现一个删除的图标(delete.html)
<i class="icon-delete"></i>删除
<i class="icon-delete">删除</i>(为了使得文字不可见,设置overflow:hidden)
  • 分析:空标签 or overflow:hidden 基线是底边缘,与文字默认不对齐

  • 思路:图标和文字高度一致 + 基线一致 = 对齐!

  • 图标和当前高度都是20px:如需合并背景图标,作者建议图标原图统一处理成一种规格再进行合并
  • 图标标签里永远有文字:借助:before或者:after伪元素生成一个空格字符
  • 潜在字符不可见,但是不使用overflow:hidden,保证基线为里面字符的基线
<style type="text/css">
    .box {
        line-height: 20px;
    }
    .icon {
        display: inline-block; 
        width:20px; 
        height:20px; 
        white-space: nowrap; 
        letter-spacing: -1em; 
        text-indent: -999em;
    }
    .icon:before {
        content:'\3000';
    }
    /* 具体图标 */
    .icon-xxx {
        background: url(img/delete.png) no-repeat center;
    }
</style>
<div class="box">
    <h4>1. 空标签后面跟随文本</h4>
    <p><i class="icon icon-delete"></i>删除</p>
    <h4>2. 标签里面有“删除”文本</h4>
    <p><i class="icon icon-delete">删除</i>看一眼时间</p>
</div>
  • 优势:
  • 不需要margin和vertical-align的垂直偏移
  • 小图标和文字对齐完全不受字体大小影响
  • 整个项目小图标和文字对齐的问题都可以解决,节约css代码,降低开发和维护成本
  • 说明:20px是经验取值,如果项目设计很大气,例如字号默认16px, 可以改成24px(详见《css世界》)
myFont.png

现象问题(令人抓狂)——> 属性介绍(透过现象看本质)——> 问题剖析(真相只有一个)——> 简单应用(化敌为友)

五、参考文献

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容