一、当line-height与vertical-align相遇,会发生很多匪夷所思的现象
首先,请看如下代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
div {
background-color: red;
line-height: normal;
}
</style>
</head>
<body>
<div>
<img src="http://a.hiphotos.baidu.com/zhidao/pic/item/72f082025aafa40fa38bfc55a964034f79f019ec.jpg"
alt="A picture" style="width:175px;height:100px" />
</div>
</body>
</html>
其效果图是这样的:
可以看到,图像的下边缘依然有一些背景颜色,极其影响页面的美观,那么,这些红色背景是怎么来的呢?
如果我们改变line-height的值,会发现图像下边缘的红色背景区域也会跟着变化,比如将line-height值从normal改为50px,其效果图为:
那么,是什么原因导致了这一变化呢?
为了说明得更清楚,我们向页面中添加另外一些辅助性的东西(一些文本);代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>隐藏文本节点分析</title>
<style>
div {
font-size: 16px;
background-color: red;
line-height:50px;
} span {
display: inline-block;
background-color: green;
}
</style>
</head>
<body>
<div>
<img src="http://a.hiphotos.baidu.com/zhidao/pic/item/72f082025aafa40fa38bfc55a964034f79f019ec.jpg"
alt="A picture" style="width:175px;height:100px" />
<span>Something xxx</span>
</div>
</body>
</html>
其页面效果为:
如果我们改变字体大小,比如将font-size值从16px改为32px;其页面效果为:
如果我们将font-size的值改成80px,效果会更明显,像这样:
我们知道,line-height决定了行内元素的高度,所以在我们只改变font-size而没有改变line-height的情况下,绿色背景的文本高度并没有任何变化。
在vertical-align的默认值为baseline的情况下,图像的下边缘始终与文本的基线对齐。
由于font-size增加了,所以内容区的高度增加了,在行内框高度不变的前提下,只能行间距减小,于是整个行内框都表现得向上移动。
然后我们删掉辅助的<span>元素,保留所有CSS设置,会发现图像纹丝不动,如下图:
也就是说,在图像右边有文本的时候,图像与其基线对齐;即使图像右边没有文本,图像也与一个假想的文本的基线对齐;由此,引出了一个“隐藏文本节点”的概念。
(注:此概念来自张鑫旭大神,如需了解原作者的观点,请移步他的个人博客,本文观点只代表我个人理解。)
vertical-align:baseline,要求一个元素的基线与其父元素的基线对齐;
如果一个元素没有基线,比如图像或者表单输入元素或者其它替换元素,则是元素的底端与父元素的基线对齐。
然而,如果父元素是一个空的div,如何确定父元素的基线?
这时,我们就可以假设,其实父元素中有一个隐藏的文本节点,只是我们看不见,它在暗中确定了父元素的基线。
这就是我对于“隐藏文本节点”的理解。
通过前面的分析,可以得知,图像下边缘多出来的一部分红色背景区域,实际上是隐藏文本节点在作怪;
更准确地说,是因为隐藏文本节点的line-height以及vertical-align:baseline在作怪。
知道了原因,我们就能对症下药地解决问题,以下是三种去除那些红色背景的方法。
方法一:由于vertical-align只能作用于行内元素和替换元素(如图像和表单输入元素),只要将图像块状化,就能解决问题。代码如下:
1 img {
2 display:block;
3 }
方法二:由于vertical-align的默认值为baseline,图像的下边缘与那些隐藏文本的基线对齐,才会多出来一些红色背景;如果改变vertical-align的值,将其设置为bottom(底线对齐),就能解决问题。代码如下:
1 img {
2 vertical-align: bottom;
3 }
方法三:设置line-height的值足够小。代码如下:
1 div {
2 line-height: 0;
3 }
我对方法三的理解:
只要一个文本存在,它就会有四条线,顶线、中线、基线、底线,而基线是根基所在,其他三条线都是根据基线定义的,所以改变字体大小,基线是不会变化的,其他三条线会相应调整位置。即使font-size为0,文本根本看不见了,但是要记得,字体大小与行内框高度没有半毛钱关系。
影响行框的只有行内框。
这里的图像行内框高度就是它自身的height,文本的行内框高度为行高,这一行的行框是包含了图像行内框的顶端与文本行内框的底端形成的。
因为图像的底端与文本的基线对齐,只要文本的行内框底端低于文本的基线,那么,图像的下边缘就总会有空隙。
当我们改变line-height,实际上就是在改变隐藏文本节点的行内框,line-height越小,它的行内框越小,当line-height等于某一数值时,文本行内框的底端与基线持平,继续减小line-height,行内框的底端在基线之上,直至line-height减小到0,行内框高度为0;但是即使line-height为0,文本是不会动的,文本还是文本,它的基线会始终与图像的“基线”对齐。
所以最简单的方法就是设置line-height为0,当一个文本元素的行内框高度为0,在构造行框的时候就只会考虑图像的行内框,图像的下边缘自然没有多余的空隙。
上面三种方法的页面效果一致,实践中可根据需要使用。
二、没有最怪,只有更怪
先上重点:
inline-block的基线是正常流中最后一个line box的基线,除非,这个line box里面既没有line boxes ,或者其本身overflow属性的计算值不是visible,则它的基线是margin底边缘。
上面这段不是人话,看不懂没关系,下面是人话:
如果一个inline-block元素里面是空的,或者它本身有overflow属性,这种情况下基线是它margin的底边缘。
如果一个inline-block元素里面不是空的(比如里面有文字或者图像),则它的基线由正常流中最后一个line box的基线决定。
举个例子,请看下面代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
.box {
display:inline-block;
width:200px;
height:150px;
border:2px solid red;
font-size: 16px;
} p {
background-color: orange;
margin:0;
}
</style>
</head>
<body>
<div class="box"></div>
<div class="box">
<p>I am the first line.</p>
<p>I am the second line.</p>
</div>
</body>
</html>
页面效果:
这是神马情况?
第一个盒子,里面是空的,所以它的基线是它的下边框。
(基线本来应该是margin的底边缘,此处为便于展示,margin为默认0,所以下边框就是它的基线;有兴趣的朋友可自己设置margin的大小看看效果。设置了margin之后,页面效果会更怪,大家可以娱乐一下。)
第二个盒子,里面有文字,所以它的基线是正常流中最后一个行框的基线决定,也就是第二行的基线。
默认情况下基线对齐,所以它们就变成这样啦。
(为便于分析,此处<p>元素的margin为0,并设置了背景颜色。)
再来玩个游戏,如果将文字的行高改变,会怎样呢?
当行高为0:
由于line-height决定了行内框的高度,从而依次绝对了行框、包含框的高度;在依次减小line-height的时候,背景颜色区域的高度相应减小;
当line-height为0时,行内框的高度为0,两个<p>元素高度都是0,它们虽然都在div这个盒子里面,但是它们不占任何空间。
前文有提到,内容区的中点将行高上下均分,当line-height为0,文字没有高度,内容区的上下部分都是0,盒子不需要在边框内部给文字留空间,所以从它内容区的中点穿过。
三、Note:
对于行内元素,vertical-align和line-height虽然不可见,但实际上【到处都是】!
也就是说,只要是行内元素,一定会受它们影响。
在遇到由vertical-align:baseline和line-height组合造成的各种无法理解的怪异现象,可以直接放大招,要么行高改为0,要么废掉基线对齐(比如底线对齐、顶线对齐就很好啊),要么两个一起改。
四、vertical-align的其他可选值
1. top,元素行内框的顶端与包含该元素的行框的顶端对齐。
2. bottom,元素行内框的底端与包含该元素的行框的底端对齐。
3. middle,元素行内框的垂直中点与父元素基线上方0.5个x(相对于父元素的字体大小)的距离处的一个点对齐;简单地说就是与x的中心点对齐。
4. text-top,元素行内框的顶端与父元素内容区的顶端对齐。
5. text-bottom,元素行内框的底端与父元素内容区的底端对齐。
6. super,将元素的内容区和行内框上移,距离未指定。
7. sub,将元素的内容区和行内框下移,距离未指定。
(注意:vertical-align的super和sub值只是单纯地上下移动元素行内框,并不改变字体大小,与<sup>和<sub>标签不同。)
8. 百分数,以行高的数值为基准计算。
9. 数字,这个好理解,不解释。
五、如果各个行内元素的vertical-align属性值各不相同,该如何处理?
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vertical-align混杂的情况</title>
<style>
div {
font-size: 16px;
background-color: red;
margin-top:100px;
} .model {
display: inline-block;
font-size: 32px;
line-height: 32px;
background-color: #000;
color: white;
} img {
border:2px solid #000;
margin-left: 20px;
} .p1,.p2,.p3,.p4 {
display: inline-block;
} .p1 {
vertical-align: top;
height: 90px;
width:120px;
} .p2 {
vertical-align: text-top;
height: 150px;
width: 200px;
} .p3 {
vertical-align: bottom;
height: 210px;
width: 350;
} .p4 {
vertical-align: text-bottom;
height: 270px;
width: 450px;
} </style>
</head>
<body>
<div class="box1">
<img class="p1" src="http://pic1.win4000.com/wallpaper/c/537b28b60619b.jpg" alt="A picture" />
<span class="model">x</span>
</div>
<p>上图,x是这个div里面的原住民,它是一个隐藏文本节点,本来我们看不见,这里只是便于分析,所以将它显示出来。</p>
<p>本来x小朋友在里面无忧无虑地玩耍,某一天,来了一个图像1,要和它玩对齐游戏,游戏规则,top对齐,于是,图像1的行内框顶端与x的行内框顶端对齐,形成一个行框,如上图。</p>
<div class="box2">
<img class="p1" src="http://pic1.win4000.com/wallpaper/c/537b28b60619b.jpg" alt="A picture" />
<span class="model">x</span>
<img class="p2" src="http://pic1.win4000.com/wallpaper/c/537b28b60619b.jpg" alt="A picture" />
<span class="model">x</span>
</div>
<p>接上回。。。</p>
<p>现在行框已经形成啦,来了一个图像2,也要玩对其游戏,游戏规则,text-top对齐,本来应该是图像2的行内框顶端与父元素的内容区的顶端对齐,但是父元素里面没有文本哪儿来的内容区呢,于是,拿x这个隐藏家伙来代替,与x的内容区的顶端对齐,如上图。</p>
<div class="box3">
<img class="p1" src="http://pic1.win4000.com/wallpaper/c/537b28b60619b.jpg" alt="A picture" />
<img class="p2" src="http://pic1.win4000.com/wallpaper/c/537b28b60619b.jpg" alt="A picture" />
<span class="model">x</span>
<img class="p3" src="http://pic1.win4000.com/wallpaper/c/537b28b60619b.jpg" alt="A picture" />
</div>
<p>接上回。。。</p>
<p>某一天,来了一个图像3,它要bottom对齐。</p>
<p>这个简单啊,bottom对齐要求图像的行内框底端与行框的底端对齐,如今大大的行框就在那里,直接把图像3放上去就好了。</p>
<p>但是这个图像3有点太高了,把行框都撑高了,没关系,行框会自动调整高度,形成新的行框。</p>
<p>但是新的行框形成之后,图像1本来是要与行框的顶端对齐的,现在也只能跟着行框往上走了。</p>
<div class="box4">
<img class="p1" src="http://pic1.win4000.com/wallpaper/c/537b28b60619b.jpg" alt="A picture" />
<img class="p2" src="http://pic1.win4000.com/wallpaper/c/537b28b60619b.jpg" alt="A picture" />
<span class="model">x</span>
<img class="p3" src="http://pic1.win4000.com/wallpaper/c/537b28b60619b.jpg" alt="A picture" />
<img class="p4" src="http://pic1.win4000.com/wallpaper/c/537b28b60619b.jpg" alt="A picture" />
<span class="model">x</span>
</div>
<p>接上回。。。</p>
<p>又有一天,来了一个图像4,这次它要text-bottom对齐,怎么办呢?</p>
<p>text-bottom对齐,就是要把图像4的行内框的底端与父元素的内容区的底端对齐,可是。。。唉,又只有拿x来代替啦,还好,x一直在原地没动呢,随时都能找到。</p>
<p>现在,图像4的行内框底端与x的内容区底端对齐。对齐之后发现,我勒个去,这个图像4怎么那么高啊。</p>
<p>没关系,行框很灵活,自动调整高度,绝对会把它装进去。</p>
<p>哎呀,行框又变高了,图像1,你得跟上啊,你是要跟行框顶端对齐的说。</p>
</body>
</html>
我对于vertical-align各种对齐方式的总结:
首先,在一个空行中,存在一个隐藏文本节点,看不见摸不着,但却以某种方式存在着,因为继承而来的font-size和line-height,它有自己的内容区和行内框。
然后,这个空行来了第一个行内元素,这个行内元素的行内框会与隐藏文本节点的行内框以指定或默认的方式对齐,从而形成一个最初的行框。
之后,行内元素一个一个地进入这一行,一个一个地按照指定或默认的方式,分别与已经形成的行框对齐,或者分别与父元素的内容区对齐。
记住,任何一个行内元素的对齐方式都是独立的,与其它行内元素没有任何关系。