11、 CSS权威指南--第 7 章 (p262)视觉格式化基础
7.1 元素框基础
不管什么元素,CSS 都假定每个元素都生成一个或多个矩形,我们称之为元素框(element box)。
各元素框的中心是内容区域,四周有可选的内边距、边框、轮廓和外边距。之所以说这些区域是可选的,是因为它们的宽度都可以设为零,即把它们从元素框上删除。
外边距、边框和内边距都分别针对每一边的属性。margin(外边距) 、 border(边框) 、 padding(内边距)。
外边距区域始终是透明的,因此透过它能看到父元素。即margin 始终是透明的,无法设置颜色。
内边距不能是负值,但是外边距可以。 即padding 值不能是负值,但margin 值可以。
7.1.1 重要概念概览
常规流动: 即渲染西方语言时,从左至右,从上到下的顺序,以及传统的HTML 文档采用的文本布局方式。
非置换元素: 内容包含在文档中的元素。
置换元素: 为其他内容占位的元素(内容不在文档中)。多数表单元素也是置换元素,如 <input type="radio"> 。
根元素:位于文档树顶端的元素。在HTML 中就是html 。
块级框: 在常规流动模式下,块级框在框体前后都“换行”,因此块级框是纵向堆叠的。display: block; 声明能把任何元素生成的框体变成块级框。
行内框: strong 或 span 等元素生成的框体。行内框前后不换行。display: inline 声明能把任何元素生成的框体变成行内框。
行内块级框: 内部特征像块级框,外部特征像行内框。行内块级框的行为与置换元素相似,但不完全相同。比如说把一个 div 元素像行内图像那样插入一行文本,这样一想你就明白了。
单元格框:略。
7.1.2 容纳块(p264)
还有一种框体需要深入说明,因为涉及的知识够多,所以单开一节。这种框体是容纳块。
每个元素的框体都是相对容纳块放置,说的简单点就是,容纳块是元素框体的“布局上下文”。
为了确定框体的容纳块,CSS定义了一些列规则。
在使用常规流动方式渲染的西文文本中,容纳块由离元素最近的那个生成列表项目或块级框的祖辈元素的边界构成。
如:
<body> <!-- p元素的块级框的容纳块是div元素的块级框,因为这是祖辈元素的框体中, 离p元素最近的,而且生成的是块级框或或列表项目。类似的,div 元素的容纳块是body 元素的框体。 因此,p元素的布局依赖 div 元素的布局,而 div 元素的布局又依赖 body 元素的布局 --> <div> <p>This is a paragraph.</p> </div> </body>
html 对应的是初始容纳块,它的独到之处是,其尺寸由视区决定,而非根元素中内容的尺寸。初始容纳块与其他容纳块的差异极小,而且通常并不重要,但是你要知道有这么一种容纳块。
7.2 调整元素的显示方式
为 display 属性设值可以影响用户代理显示元素的方式。
7.2.1 改变显示方式
通过display 可以改变元素的显示方式,但不改变元素的本性。也就是说,把段落生成的框体变成行内框,并不会把段落变成行内元素。(改变的只是框体,不是元素)
了解这些知识后,下面深入讨论不同的宽体: 块级框, 行内框, 行内块级框,列表项目框。
7.2.2 块级框
box-sizing 属性能作用于所有能设定width 或 height的元素,它定义了应该如何计算一个元素的总宽度和总高度。
在 CSS 盒子模型的默认定义里,你对一个元素所设置的 width 与 height 只会应用到这个元素的内容区。
box-sizing 属性可以被用来调整这些表现:
content-box 是默认值。如果你设置一个元素的宽为100px,那么这个元素的内容区会有100px 宽,并且任何边框和内边距的宽度都会被增加到最后绘制出来的元素宽度中。
border-box 告诉浏览器:你想要设置的边框和内边距的值是包含在width内的。也就是说,如果你将一个元素的width设为100px,那么这100px会包含它的border和padding,内容区的实际宽度是width减去(border + padding)的值。大多数情况下,这使得我们更容易地设定一个元素的宽高。
注: border-box不包含margin。
<!DOCTYPE html> <html> <head> <style> .d1, .d3, .d5 { margin: 26px; height: 100px; width: 100px; border: 2px solid red; } .d2, .d4, .d6 { margin-top: 15px; height: 60px; width: 100%; } .d2 { border: 5px solid grey; margin-left: -5px; /* 默认值 content-box就是元素的width和height决定了元素的宽高, 这意味着元素的border和padding等不能算在元素的width和height中 , padding和border的改变不能改变width和height的值。 即元素的width 属性值 = content宽 */ box-sizing: content-box; } .d4 { border: 5px solid grey; /* 设置border-box 时,可以理解为,固定了border 的边缘,不会再向外部扩展,只会向内和content 分高宽值 即padding左右宽之间的距离不变 即元素的width 属性值 = 左右border宽 + 左右padding宽 + content宽 */ box-sizing: border-box; } .d6 { border: 5px solid grey; /* 元素的width 属性值 = 左右padding宽 + content宽 */ box-sizing: padding-box; } </style> </head> <body> <div class="d1"> <div class="d2"></div> </div> <div class="d3"> <div class="d4"></div> </div> <div class="d5"> <div class="d6"></div> </div> </body> </html>
7.2.3 横向格式化
使用content-box 时,为 width 设定的值是内容区的宽度,而不是整个元素框的可见宽度。
设为 border-box 时,这两种情况下,常规流动方式下块级框各组成部分的横向尺寸始终等于容纳块的宽度。
7.2.4 横向格式化属性
横向格式化属性有七个,margin-left , border-left , padding-left , width ,padding-right , border-right , margin-right 。
这七个属性影响块级框的横向布局,这七个属性的值加在一起等于元素容纳块的宽度。而这一宽度通常影响块级元素的父元素的 width 值(因为块级元素的父元素几乎都是块级元素)。
这七个属性只有内容区和margin 的宽度可以设置值为 auto (即width 和 margin 要么设置为 auto ,要么设置具值)。剩下的属性要么设置具体值,要么使用默认值(零)。
7.2.5 使用 auto
假设七个属性值一定,比如是500px , width 为100px , margin-right 为 100px , padding 和 border 未设置(即为零),margin-left 的值设为 auto ,那么它最终的值是 300px 。
因为auto 的那个属性值的具体长度要能满足元素框的宽度等于父元素的宽度。
从某种意义上说, auto 可用于补全综合所缺的尺寸。
<!DOCTYPE html> <html> <head> <style> div { width: 500px; } p { margin-right: 100px; margin-left: auto; width: 100px; } </style> </head> <body> <div> <p></p> </div> </body> </html>
按照CSS 权威指南的说法,如果上述代码中的 margin-left 设为 100px 的话, margin-right 将被强制设为 auto , 窗口表现上似乎是这样,但盒模型没有显示,只是提示 margin-right 的值依旧为 100px 。
<!DOCTYPE html> <html> <head> <style> div { width: 500px; height: 60px; background-color: antiquewhite; } p { margin-left: 100px; margin-right: 100px; width: 100px; height: 36px; background-color: azure; } </style> </head> <body> <div> <p></p> </div> </body> </html>
如果左右margin 都设为 auto ,那么 width 的值是满足总宽度所需的任何值。(width在父元素中会左右居中)
如果三个属性值都设置为 auto ,那么 width 将等于 父元素 width 。
如果设置margin-left 为具体值,width 设为 auto ,不设置margin-right ,那么width 将填充剩下所以的空间。
7.2.6 多个auto 、7.2.7 负外边距
略
7.2.8 百分数
百分数的意思是,把元素的宽度设为容纳块宽度的占比。
7.2.9 置换元素
块级置换元素的处理方式,前面的针对非置换块级元素的规则都成立,不过有个例外: width 为 auto 时,置换元素的 width 等于内容自身的宽度。
7.2.10 纵向格式化
略
7.2.11 纵向格式化属性
在常规流动模式下,如果把margin-top 或 margin-bottom 设置为 auto ,二者都自动计算为 0 , 这也就意味着流动模式下,元素无法轻易在容纳块中纵向居中。
height 要么设为 auto ,要么设为某种类型的非负值。
7.2.12 百分数高度
在常规流动模式下,如果把块级框的高度设为百分数,百分数是相对框体的容纳块的高度而言的。
然而,如果未明确容纳块的高度,那么百分数高度将被重置为 auto 。
如果把margin-top 或 margin-bottom 设为百分数,那么依旧相对于容纳块的width 属性值。
<!DOCTYPE html> <html> <head> <style> div { width: 100px; height: 100px; background-color: antiquewhite; } p { margin-left: 25%; margin-right: 25%; /* 查看盒模型,margin-top 值为 10% * 100px(容纳块width) margin-bottom亦然 */ margin-top: 10%; margin-bottom: 25%; width: auto; height: 10px; background-color: rgb(97, 15, 230); } </style> </head> <body> <div> <p></p> </div> </body> </html>
7.2.13 自动调整高度
在常规流动模式下,声明 height: auto 的块级框是最简单的,此时,框体的高度恰好能放得下里面的内容。
常规流动模式下的块级框如果高度是自动调整的,而且子代都是块级元素,那么默认的高度是从最上边的那个块级子代元素的上边框外侧(border-top)到最下边那个块级子代元素的下边框外侧之间的距离。
因此,子元素的外边距(margin)游离在所属元素的外部。
然而,如果块级元素有上内边距或下内边距(上下padding),或者由上边框或下边框(上下boder),那么其高度是从最上边那个子元素的上外边距的外边界到最下边那个子元素的下外边距的外边界之间的距离。
<!DOCTYPE html> <html> <head> <style> div { width: 100px; height: 100px; background-color: antiquewhite; } p { height: auto; background-color: rgb(97, 15, 230); } </style> </head> <body> <div><!-- 两个 p 元素只能看到一个,因为此时的元素 height 刚好能放下里面的内容, 第二个p 元素内容为空,故而height 为 0 ,换言之,值为auto 时,浏览器根据内容自动调节高度 --> <p>我歌且谣</p> <p></p> </div> </body> </html>
7.2.14 折叠纵向外边距
纵向格式化的另一个重要特征是,相邻的纵向外边距会折叠。只有外边距有这种折叠行为。较小的外边距会被较大的外边距折叠(重叠)。
内边距和边框,绝不与任何区域折叠。
为容纳块添加边框或内边距之后,子元素的外边距便会包含其中。
7.2.15 负外边距和折叠
如果负外边距导致重叠,后续元素的背景色可能会遮盖前面元素的内容,因为浏览器是从头到尾按顺序渲染元素的,在常规流动模式下,如果文档中文娱后面的元素与前面的元素重叠了,理应遮盖元素的内容。
7.2.16 项目列表
列表记号可以放在刘表项目内容区的外部,也可以作为行间内容,放在内容区的开头,这取决于 list-style-position 属性的值。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>DOC</title> <style> li:first-child { background-color: antiquewhite; list-style-position: inside; } li:last-child { background-color: beige; list-style-position: outside; } </style> </head> <body> <ul> <li>inside</li> <li>outside</li> </ul> </body> </html>
7.3 行内元素
本节所述的行为均不适用于表格元素,表格及其内容的行为与块级元素和行内元素有极大的差异。
7.3.1 行布局
各行的边框恰巧与上下边重合,这种情况仅当行内文本没有内边距才会发生。
注意,边框稍稍有点重合,例如,第一行的下边框正好位于第二行上边框的下方。
7.3.2 基本属于和概念
深入讨论之前,先学习行内布局的一些术语。
匿名文本 : 不在任何行内元素中的字符。注意,空格也是匿名文本的一部份,因为空格也是一种字符。
字体框 : 由字体具体定义,也叫字符框,font-size 属性控制字体框的高度。
内容区 : 非置换元素,内容区可以是各字符的字体框连在一起构成的方框。对置换元素来说,内容区是元素自身的高度加上内外边距和边框。
行距 : 行距是 font-size 和 line-height 之差, 二者之间的差值除以 2之后分别添加到内容区的上部和下部。内容区多出来的这部空间叫半行距。(只有非置换元素有行距)
行内框 : 内容区加行距后得到的方框。对非置换元素来说,行内框的高度等于 line-height 属性的值,对置换元素而言,行内框的高度等于内容区的高度,因为置换元素没有行距。
行框 : 过一行中各行内框最高点和最低点的方框。
此外,CSS 还定义了一些行为和有用的概念:
- 内容区相当于块级框的内容框。
- 行内元素的背景填充在内容区加内边距所在的区域里。
- 行内元素的边框在内容区外的内边距外侧。
- 非置换行内元素的内边距、边框和外边距在对应的方框上没有纵向效果,即对行内框的高度没有影响。
- 然而,置换元素的外边距和边框对行内框的高度有影响,进而对元素所在的行框高度也有影响。
7.3.3 行内格式化
不管有没有显式声明,所有元素都有 line-height 值。
块级元素可以有 line-height 值, 但是这个值将应用于块级元素内部的内容上(不管在不在行内元素中)。从某种程度上讲,块级元素中的各文本行本身就是行内元素,
不论是否真的在行内标签中都是如此。如果愿意,可以想象成有个虚构的标签,如下:
<body> <p> <line>This is a paragraph with a number of</line> <line>lines of text which make up the</line> <line>contents</line> </p> </body>
尽管 line 标签并不存在,但是段落的行为就像有这些标签一样,每行文本都从段落上继承样式。。所以,你可以为块级元素设定 line-height 值,这样就无需为每个行内元素(虚构的或真实的)设定 line-height 值了。
7.3.4 行内非置换元素
行框的构成 :
对非置换元素或匿名文本来说,font-size 值决定内容区的高度。
<!DOCTYPE html> <html lang="en"> <head> <title>Document</title> <style> p { font-size: 12px; line-height: 12px; } strong { font-size: 24px; } </style> </head> <body> <p>This is text, <em>some of which is emphasized</em>,plus other text<br> which is <!-- 因为行内框的顶边在元素的内容区内部,所以加粗元素的内容超出了行框 --> <strong>strongly emphasized</strong> and which is <br> larger than the surrounding text. </p> </body> </html>
结合上述代码,详细阅读 p294 和 p295 部分
纵向对齐:
vertical-align 各个关键字值的效果 :
top : 元素行内框的顶边与所在行框的顶边对齐。
bottom : 元素行内框的底边与所在行框的底边对齐。
text-top : 元素行内框的顶边与元素内容区的顶边对齐。
text-bottom : 元素行内框的底边与父元素内容区的底边对齐。
middle : 元素行内框的纵向中点与父元素基线以上 0.5ex 处的点对齐。
super : 向上移动元素的内容区和行内框。距离由用户代理确定。
sub : 与 super 类似,只不过是向下移动。
<percentage> : 向上或向下移动元素,移动的距离等于声明的百分数乘以元素的 line-height 。
控制行高 :
<!DOCTYPE html> <html lang="en"> <head> <title>Document</title> <style> p {line-height: 1em;} big { font-size: 250%; line-height: 1em; } </style> </head> <body> <p>修改行内元素的 line-height 值可能会导致一行中的文本与另一行重叠, 那么有没有<big>一般性的方法</big>,能让 line-height 的值不导致行之间有重叠呢? 一种让行之间不重叠的方法是为字号有变化的元素设定单位为 em 的行高。 </p> </body> </html>
我们把 big 元素的 line-height 值设为 1em ,因此,big 元素的行高将与字号相等。因为,line-height 是相对元素自身的 font-size 而言的。
控制行高 :
<!DOCTYPE html> <html lang="en"> <head> <title>Document</title> <style> p {line-height: 1em;} span { border: 0.2em solid red; line-height: calc(1em + 0.8em); /* 为什么要1.8em 才行,为什么不是1.4em ,为什么是4倍,而不是两倍 */ } </style> </head> <body> <p>修改行内元素的 line-height 值可能会导致一行中的文本与另一行重叠, 那么有没有一般性的方法,能让 line-height 的值不导致行之间有重叠呢? 一种让行之间不重叠的方法是为字号有变化的元素设定单位为 em 的行高。然后将之增加的高度,增加到行高上。 所以边框是依附在<span>哪里呢?</span>难道不应该在增加边框后使得行高增加吗?(行高 line-height 的高度组成是不包括边框的, 所以边框虽然依附在内容区,但是与行高无关,换言之二者重叠依附在同一位置,所以不会影响到行框??) 行内元素可以设置padding ,border,以及水平方向的margin. 不能设置宽高以及垂直方向的间距(margin)。 </p> </body> </html>
基线与行高 :
按比例设定行高 :
实践证明,line-height 的值最好是纯数字,这是因为纯数字相当于比例因子,而这个因子能被继承,而不计算具体的值。
body { line-height : 1.5; } /* line-height 设为 font-size 的1.5 倍 */
这个比例因子将沿着元素继承关系一层一层向下传递,作为各元素 font-size 值的乘数。如果 font-size 继承是一致的,那么行高也是一致的。
加上盒模型属性 :
内边距、外边距、和边框也都可以应用到行内非置换元素上,但是行内元素的这些属性并不影响行框的总体高度。
边框的边界由 font-size 控制,而不受 line-height 影响。
内边距并不影响内容区的高度,因此也不影响元素行内框的高度。类似的,行内元素的边框也不影响行框的生成和布局方式。
外边距不会添加到行内非置换元素的上部和下部,因此也就不影响行框的高度。不过行内元素的两端确受外边距的影响。
改变断行行为 :
一个行内非置换元素分成多行显示时,用户代理将视为断成多块的一长行,每换一行就多一块。这其实是默认行为,可以通过 box-decoration-break 属性改变。
box-decoration-break 取值 slice 、 clone 。默认值 slice 。
clone 把元素各片段视作单独的框。与默认值想比,除了 box-decoration-break 的属性值不同,其他标记和样式都一样。
字型与内容区 :
行内非置换元素的“绘制区”由用户代理确定。如果用户代理把字体框的高度当作内容区的高度,那么行内非置换元素的背景高度等于字体框的高度(即 font-size 的值)。
如果用户代理使用的字体的最大上伸和下沿值,那么背景的高度可能比字体框高或矮。此时,如果把行内非置换元素的行高设为 1em ,背景依然会与其他行重叠。
7.3.5 行内置换元素
行内置换元素自身是有高度和宽度的。
行框的高度将正好容纳置换元素及其盒模型属性设定的值。也就是说,置换元素的行内框包含整个元素,包括内容、外边距、边框和内边距。
line-height 对图像的行内框没有影响。但是置换元素的 line-height 属性仍然有值,这是因为纵向对齐时,有这个值才能正确定位元素的位置。
例如,vertical-align 属性的百分数值就是相对元素的 line-height 计算的。
在纵向对齐中,图像自身的高度没有任何意义,一切都由 line-height 的值决定。
加上盒模型属性 :
内边距和边框会影响行框的高度,因为它们是行内置换元素内框的一部分(这一点与非置换元素不同)。
只有通过负外边距才能让行内置换元素叠加到其他行上,正是因为这样,行内置换元素生成的行内框通常才假定为行内块。
置换元素与基线 :
行内置换元素默认与基线对齐。
置换元素自身没有基线,退而求其次,把置换元素的行内框底边与基线对齐。因此,与基线对齐的其实是外边距(margin)的边界。
7.3.6 行内块级元素
行内块级元素与其他元素和内容的关系按照行内框处理。也就是说,它在一行中的布局方式跟图像一样。
实际上,行内块级元素是当做置换元素进行格式化的。这意味着,行内块级元素的底边默认是与文本的基线对齐的,而且内部不会断行(行内块级元素是不断行的)。
行内块级元素中的内容视作块级元素进行格式化,width 和 height 属性可以应用到行内块级元素上(box-sizing 属性也可以),因为这些属性都可以应用到块级元素或行内置换元素上,而且如果行内块级元素比周围的内容高,行的高度也会随之增加。
7.3.7 流动显示方式
声明为 display: flow 的元素常规情况下使用块级布局。如果再加上 inline 。则生成行内框。
如 :
ele { display: flow; } /* 转化为块级框 */
ele { display: block flow; } /* 转化为块级框 */
ele { display: inline flow; } /* 转化为行内框 */
而 display: flow-root 则始终生成块级框,而且内部会生成新的块级格式化上下文。
display 属性的新旧值对照 :
/* 旧值 新值 block block flow inline inline flow inline-block inline flow-root list-item list-item block flow inline-list-item list-item inline flow table block table inline-table inline table flex block flex inline-flex inline flex grid block grid inline-grid inline grid */
7.3.8 contents 显示方式
display 还新增了一个带魔法的值, contents 。表示被装饰的元素不再参与页面的格式化。相当于把它的子元素提升到当前的层级。
<!DOCTYPE html> <html lang="en"> <head> <title>Document</title> <style> #u-1 {border: 1px solid red;} #u-1 li {border: 1px solid silver;} #u-2 { border: 1px solid red; display: contents; } #u-2 li {border: 1px solid silver;} </style> </head> <body> <ul id="u-1"> <li>The first list item.</li> <li>List Item II:The Listening.</li> <li>List item the third.</li> </ul> <ul id="u-2"> <li>The first list item.</li> <li>List Item II:The Listening.</li> <li>List item the third.</li> </ul> <p>如果把 display: contents 应用到 ul 元素上,用户代理会按照文档中没有 #u-2 的 ul 元素那两行进行渲染。#u-2就像从未出现过一样。 </p> </body> </html>
7.3.9 display 的其他值
display 还有很多值,本书不再讨论。
与旁注(ruby)相关的值本书完全不涉及,因为讲起来一本书也不够,而且截止至2017年末支持有限。
7.3.10 计算值
如果元素是绝对定位的,float 的值为 none 。
对浮动或绝对定位的元素来说,display 的计算值由声明的值确定。
对根元素而言,不管声明为 inline-table ,还是 table,得到的计算值均为table;而声明为 none 时,得到的就是 none ;其他值得到的计算值均为 block 。
7.4 小结
很多时候,看起来没有道理甚至荒谬的规则都是事出有因的,为的是防止文档显示得乱七八糟,不符合我们的预期。