第一次写长文,如有不对请指正。有好的建议也欢迎提出。
02-28补丁:在"巩固练习"第四节添加了文字环绕图片的例子。
02-29补丁:在"行框基线">"第二步">"情况一",修改了基准元素对行框基线影响的描述,应该会更容易让人理解,同时解决了之前的疑惑。
目录
第二步:看基准元素的vertical-align是否为top或bottom
情况一:基准元素的vertical-align不为top或bottom
情况三:基准元素的vertical-align为bottom
行内格式化上下文
要讨论行内元素(inline-level box)的布局和排列规则,我们首先需要知道一个概念,就是行内格式化上下文(inline formatting context,IFC)。在IFC中,大多数情况下,行内元素从包含块的顶部开始按水平方向依次排列。这些元素的水平方向上的margin、border和padding会占据空间。垂直方向上的外边距margin、边框border、内边距padding不会实际占据空间,仅会影响显示效果。
如图,两个span元素依次排列,各自带有不同的背景颜色和1px的黑色边框。
将margin、padding和border都设为8px后,可以发现内容区域(大致就是选中的蓝色部分)和盒子边框的水平距离为8px。两个行盒之间16px的空白部分为两个行盒的左右margin。垂直方向上,设置padding和border只造成了显示上的变化,并没有影响文本垂直方向上的位置。
在IFC中,行框(line-box)是包含一行内所有行内元素的矩形区域。
行框的宽度一般由包含块确定,但也可能受浮动元素的影响。
行框的高度总是正好能容纳所有行内元素。对于行框来说,替换元素和行块盒的高度是其margin-box的高度,行盒的高度是其line-height。根据其包含的行内元素垂直对齐方式(vertical-align)的不同,行框的高度也会发生变化。
如果行内元素在水平方向上无法容纳在单个行框内,它将分布在两个或多个垂直堆叠的行框中。所以多行文本实际上会形成一系列垂直堆叠的行框。
基线(baseline)
行内元素可能包含文本和其他行内元素,换句话说,任何文本都会被行内元素包含,即使是直接写在块盒中的文本,也会被包含在一个或多个匿名行盒中。
对于嵌套的行内元素,其实相当于在外部行内元素的内部生成了一个行框,需要计算内部行框的基线作为外部行内元素自身的基线。
对于只含有文本的情况,行内元素的基线取决于文本基线。
文本基线(text baseline)
基线是字体的属性。对于大多数字体,会定义一个主要基线作为参考线。我们需要关注的主要是alphabetic,也就是我们平时说的基线。
alphabetic:穿过小写x的底部,通常情况下会作为主要基线。
middle:穿过小写x的中心。
math:位于字体高度的中央。
over-edge:文字的顶线。
under-edge:文字的底线。
行内元素的基线
默认情况下,行框中的所有行内元素都会让自身的基线与行框的基线对齐。在考虑行框的基线的计算之前,我们先要确定常用的行内元素的基线。
行块盒:如果其中没有内容或overflow不为visible,基线就是它的margin-bottom,否则基线是最后一个行框的基线:行块盒有部分块盒的特性,可以理解为在行块盒中嵌套了行盒,行盒继承了行块盒的line-height。行块盒中的内容如果无法显示在一行内就会换行,这就会行块盒内部形成多个行框。
我们定义一个行盒和一个行块盒,并将行块盒的基线设置为行框的基线,代码如下。
<body>
<div>
<span class="content">Lorem ipsum dolor sit amet. x</span>
<span class="strut">x</span>
</div>
</body>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
div {
height: 100px;
background-color: red;
margin-top: 50px;
}
.strut {
height: 100%;
display: inline-block;
vertical-align: baseline;
border-right: 1px solid black;
font-size: 50px;
}
.content {
font-size: 30px;
background-color: aquamarine;
vertical-align: baseline;
line-height: 30px;
border: 1px solid black;
}
效果如下:可以看到行块盒中小写x的底部与行盒中小写x的底部是对齐的。
接着我们给strut类添加一个“overflow: auto;”(当然别的也行)
效果如下:行盒中小写x的底部与行块盒的底部对齐。
最后测一下行块盒中没有内容的情况。
替换元素:基线是它的margin-bottom。
将img的基线设置为行框的基线。
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
div {
background-color: red;
}
img {
width: 300px;
}
<body>
<div>
<img src="./login-background.jpg" alt="">
x Lorem ipsum dolor sit amet.
</div>
</body>
可以看到生成的匿名行盒中的文字基线与图片的底部是对齐的。
行盒:行盒的基线是其中的最低文本基线。因为我们知道一种字体不一定支持所有的文字,所以即使是一个行盒中的文本,也可能存在多种字体。
行高(line-height)
对于行盒来说,line-height用于计算行框的高度。
文字与行高
字体的实际高度
字体的实际高度是字体顶线和底线之间的距离。
字体的实际高度受font-family、font-size等的影响,并不是说将字体大小设置为30px,字体的实际高度就会是30px。
我们把文字基线之上顶线之下的高度简称为A,基线之下底线之上的部分称为D。
行距(leading)和半行距(half-leading)
我们把行距简称为L。
L = line-height - AD
半行距是行距的一半,它会分别添加到文字的顶线之上和底线之下。(注:行距可以为负数)
最小行高
字体通常会定义一个最小行高,用于确定文本在行内元素中的垂直对齐方式。
最小行高会随着font-family、font-size等的变化而变化。
如果设置的行高小于字体的最小行高,浏览器会根据字体的最小行高来进行计算,以确保文本的显示效果正确。
行盒的内容区域(content-box)
如果行盒中只有文字,它的内容区域的高度就是字体的实际高度。
对行盒设置padding、border、margin也是从其内容区域往外延伸。
文字与行高小结
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
background-color: yellow;
height: 200px;
margin: 50px 0 0 50px;
width: fit-content;
}
span {
line-height: 150px;
padding: 30px 0;
font-size: 50px;
background-color: aqua;
border: 1px solid black;
}
<body>
<div class="container">
<span>Lorem.</span>
</div>
</body>
块盒的行高
块盒的行高用于指定元素内行框的最小高度。
可以理解为在行框的最前面存在一个叫“支柱(strut)”的看不见的虚拟盒子。它的高度为块盒的行高,宽度为零,且其中有一个字体大小为块盒字体大小且宽度为零的看不见的文字。
示例:如果块盒中存在一个空的行块盒,会发现虽然行块盒没有高度但块盒却有21px的高度。
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
div {
background-color: red;
margin-top: 50px;
}
span {
display: inline-block;
}
<body>
<div>
<span></span>
</div>
</body>
行框基线
第一步:找基准元素
在行框内的所有行内元素中(包括支柱)找到基线最低的那个。
把这些行内元素单独放在行框中且vertical-align为baseline,基线最低的元素就是基准元素。
第二步:看基准元素的vertical-align是否为top或bottom
情况一:基准元素的vertical-align不为top或bottom
基准元素的vertical-align属性决定行框基线的位置。(2024-02-29修改)
与其他行内元素一样,它会按照vertical-align设置的值与行框对应的位置对齐。
如果设置vertical-align为20px,它会认为自身的基线与行框基线上方20px对齐,于是行框基线就会出现在基准元素自身基线下方20px。如果你想用%取值也是类似的,只要知道%取值参考的是行内元素自身的line-height属性。
如果设置了vertical-align为sub,它会认为自身的基线与行框的下标基线对齐,于是行框基线就会略微上移(与父元素字体大小有关)。
如果设置了vertical-align为middle,它会认为自身高度的一半与行框基线上方半个父元素x-height高度的位置对齐,所以行框基线会上移到基准元素高度一半以下一点。
。。。
可以想象成先把基准元素放进行框中,根据它的vertical-align确定了基线的位置后,再把其他行内元素放进行框中,并根据各自的垂直对齐方式与行框的基线对齐。如果在对齐的过程中发现原本的行框高度不足以容纳新加入的行内元素,就调整行框的高度以容纳所有的行内元素。
如果行框的高度不会因为其他元素垂直对齐方式的变动而变动,变更基准元素的vertical-align不会影响它在行框中的位置。
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
background-color: yellow;
margin: 50px 0 0 50px;
}
#span1 {
display: inline-block;
height: 50px;
font-size: 30px;
vertical-align: baseline;
background-color: aqua;
}
#span2 {
display: inline;
font-size: 30px;
line-height: 150px;
vertical-align: baseline;
background-color: blueviolet;
}
#span3 {
display: inline-block;
width: 100px;
height: 50px;
font-size: 30px;
vertical-align: baseline;
background-color: brown;
}
<body>
<div class="container">
<span id="span1">x Lorem, ipsum dolor. x</span>
<span id="span2">x Lorem, ipsum dolor. x</span>
<span id="span3"></span>
</div>
</body>
左图为原代码,右图将span2的vertical-align修改为middle。
如果是行框上方高度不足导致行框高度变化,基准元素距离行框底部的距离不变。
左图为原代码,右图将span3的height修改为120px。
如果是行框下方高度不足导致行框高度变化,基准元素距离行框顶部的距离不变。
左图为原代码,右图将span1的height修改为120px。
情况二:基准元素的vertical-align为top
首先基准元素会与行框的顶部对齐。
接着找出在顶部对齐的情况下,vertical-align为baseline的基线最低的元素。
让这个元素与行框的顶部对齐,然后将它的基线作为行框的基线。
对之前的代码进行一些小改动之后,就得到了上图的效果。span2依然作为基准元素,但vertical-align修改为top。剩下的两个行块盒、一个匿名行盒和一个支柱盒子中,没有内容的span3的基线最低,所以行框的基线就是span3的基线。
情况三:基准元素的vertical-align为bottom
首先基准元素会与行框的底部对齐。
接着找出在底部对齐的情况下,vertical-align为baseline的基线最高的元素。
让这个元素与行框的底部对齐,然后将它的基线作为行框的基线。
第三步:其他行内元素对齐基线
其他的行内元素会根据自身的vertical-align属性与行框基线对齐或与行框的顶部或底部对齐。
如果vertical-align为baseline,就是以自身基线与行框基线对齐。
如果vertical-align为middle,就是以自身中线与行框基线对齐。
如果vertical-align为top,就是以自身顶线与行框顶部对齐。
如果vertical-align为bottom,就是以自身底线与行框底部对齐。
。。。
巩固练习
一、图片下方的小缝
这是因为支柱的存在。支柱的基线与图片底部对齐,而支柱的行高过大撑开了行框。
可以将行高适当减少,只要文字基线以下的实际高度加上半行距(可以为负数)小于零就行。
也可以将图片的vertical-align设置为bottom。
二、继承行高
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.container {
background-color: yellow;
line-height: 100px;
height: 100px;
}
span {
background-color: aqua;
font-size: 30px;
}
<body>
<div class="container">
<span>Lorem ipsum dolor sit amet.</span>
</div>
</body>
行框的高度稍大于设定的100px,这也是因为支柱的存在。
虽然支柱的行高与span元素相同,但从块盒继承的font-size小于30px。这种情况下,支柱在对齐基线的过程中,因为实际字体高度较小,附加在字体下方的半行距较span元素中的文本大,从而撑开了行框。
三、多行文本垂直居中
<body>
<div class="container">
<span class="strut"></span>
<div class="content">Lorem ipsum dolor sit amet consectetur adipisicing elit. Hic, vero minus consectetur
similique repellendus,
unde ea, quos id distinctio numquam obcaecati sapiente. Dolore fuga reiciendis praesentium sint. Dolor,
eveniet placeat?</div>
</div>
</body>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
width: 600px;
background-color: red;
margin-top: 50px;
line-height: normal;
}
.strut {
display: inline;
line-height: 200px;
vertical-align: middle;
}
.content {
display: inline-block;
vertical-align: middle;
}
用行块盒原理也是一样的,但要注意空白折叠。
四、文字环绕图片(2024-02-28补充)
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.height-provider {
width: 0;
height: 80px;
float: left;
}
.img-wrapper {
float: left;
clear: both;
margin-right: 10px;
width: 30%;
}
.img-wrapper img {
width: 100%;
}
p {
line-height: 2;
background-color: aqua;
}
<body>
<div class="height-provider"></div>
<div class="img-wrapper">
<img src="./login-background.jpg" alt="">
</div>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Hic sunt exercitationem quis, dignissimos, aliquam velit
doloribus est expedita ex deleniti nostrum, dolore amet repudiandae error natus blanditiis consectetur quibusdam
omnis.
Veniam aliquam officiis praesentium voluptatum soluta et dolore alias accusantium mollitia ut perspiciatis fuga
rerum dignissimos, sit accusamus consectetur reprehenderit amet odit voluptatibus quod quasi iusto? Quia
exercitationem laboriosam culpa.
Illum, architecto reiciendis. Reiciendis cupiditate itaque provident quo commodi explicabo inventore beatae
harum ipsum molestiae consectetur, deleniti laborum numquam, necessitatibus nostrum libero iste dolore cumque,
cum quis maxime sunt molestias?
Nesciunt odio officia quia dicta blanditiis ratione ea totam possimus deleniti! Placeat earum quisquam
praesentium at debitis? Assumenda mollitia, sit accusantium earum dignissimos a sunt officiis tenetur molestiae
fugiat illo!
Cum, sint recusandae alias asperiores enim quo fugit eos rem laudantium corrupti quibusdam voluptate!
Accusantium similique nobis, quod autem deserunt consequatur architecto eius voluptatum saepe fuga quae!
Praesentium, sed quos.
Modi minima cumque ea ab mollitia perspiciatis ipsa quae at tempore dolores, earum deleniti magnam officiis quos
odit eius ipsum. Exercitationem dolorem libero temporibus veritatis. Dolor perferendis minima similique soluta.
Pariatur, dolor accusamus. Corrupti, optio! Laboriosam iusto asperiores culpa. Dolore, culpa earum vitae
suscipit ea necessitatibus soluta tenetur fuga, fugiat ullam ipsa, alias numquam eveniet commodi nihil delectus.
Autem, quasi.
Maiores, dolorem blanditiis amet vitae aspernatur minima, sequi obcaecati explicabo dolore repellat fuga? Atque
dolores autem nostrum accusantium, voluptatum nihil ab optio ratione dolorum dolorem reiciendis impedit ad
earum. Illo.
Harum cum natus sequi? Commodi illo molestias, nihil quasi consequuntur culpa voluptatibus, quo vero suscipit
officia necessitatibus similique reiciendis labore iste accusamus aspernatur officiis facilis. Fugiat magni
alias incidunt placeat?
Perferendis vel, voluptatibus, officiis similique ducimus aliquam tenetur expedita ea odit dolore fugiat
voluptatum facere, aspernatur rem. Cupiditate distinctio vel, ea cum officia et veritatis placeat. Qui explicabo
libero voluptatibus.
Tempora repudiandae dolore error consequuntur aperiam a praesentium culpa harum assumenda ullam officiis,
distinctio eius esse commodi temporibus placeat libero, ex incidunt molestias quibusdam vero similique rem
laborum? Fugit, facere.
Dolorem cum pariatur alias sequi dicta quisquam doloremque minus consequuntur, aperiam repudiandae officia, sint
qui. Eveniet nobis voluptate rem, est laborum quis ex labore ipsum? Quisquam, voluptates? Vero, quisquam
molestiae.
Ipsum repellendus praesentium aliquid eos veritatis delectus commodi voluptatum voluptatem, eum corporis
aspernatur nesciunt ratione nostrum temporibus officiis labore quam vero laudantium ipsam. Minima neque sit
architecto aut nisi est!
Omnis corporis, architecto soluta cupiditate totam laboriosam similique facilis, tempore nam, beatae officiis
recusandae placeat nisi necessitatibus at id! Dicta dignissimos impedit est molestiae quos accusantium nobis ut
amet rerum?
Exercitationem beatae, reprehenderit aliquam dolorum debitis quo soluta ipsa expedita velit aspernatur odio
libero eligendi nobis minima perspiciatis tempora voluptate ut illo voluptatum blanditiis. Error ipsam id
exercitationem corporis hic?</p>
</body>
float属性最开始就是为了做文字环绕图片的效果,后来被广泛用于布局。虽然有CSS3之后有了更多的选择,但float依然是兼容性最好的,所以还是有必要了解。这里就是利用了行框宽度受浮动元素影响的原理。虽然不设置clear属性,块盒"看不见"浮动元素,但行框"看得到"。如果块盒所在的位置存在浮动元素,行框就会被挤到后面。
不太重要的补充内容
因为在印刷领域,字母的中线位于字形的中间位置偏下一点。
在网页设计中,为了模拟传统排版的外观和感觉,浏览器通常会将行块盒和替换元素的中线设定在元素盒模型的垂直中心位置偏下一点。这样做可以确保文本和其他元素在视觉上看起来更加居中和对齐,与传统印刷排版更为一致。