CSS知识点梳理(7)-基本视觉格式化

基本框

CSS 假设每个元素都会生成一个或多个矩形框,称之为元素框(element box)。每个元素框 中心有一个内容区(content area)。内容区周围有可选的padding,border,outline,margin,将它们的宽度设为 0,可以将它们从元素框去除。

  • 内边距(padding),不能为负值,内容的背景色会应用到内边距。
  • 外边距(margin),通常是透明的,从中可以看到父元素的背景色,外边可以为负值。
  • 边框(border),如果没有设置颜色,则取元素内容的前景色。如果边框样式有某中间隙,则可以通过间隙看到元素的背景色(换句话说,边框与内容和内边距具有相同的背景)。边框的宽度不能为负值。
  • 轮廓(outline),轮廓不占据空间,它被描绘与内容之上。

快速复习

  • 正常流(Normal flow):在西方语言中从左到右,从上到下的文本渲染,也是传统 HTML 文档的文本布局。要注意,在非西方语言中,流向可能发生变化。大多数元素都在正常流中,唯一让一个元素脱离正常流的方法就是让其成为浮动元素、定位元素、或者是弹性盒子、网格布局元素。本章讨论的元素都在正常流中。
  • 不可替换元素(Nonreplaced element): 其内容包含在文档中的元素。例如,p段落是不可替换的元素,因为其文本内容位于该元素本身内。
  • 可替换元素(Replaced element):这是充当其他内容占位符的元素。一个典型的例子就是img元素,它只是指向一个图像文件,这个文件将插入到文档流中该img元素本身所在的位置。大多数表单元素也是可替换的。
  • 根元素(Root element): 文档树顶端的元素。在 HTML 文档中,根元素是html元素。
  • 块级盒子/框(Block box): 这是一个由元素(如p,div)生成的框。这些块级框在正常流中,会在其框之前和之后生成“换行”,所有处于正常流中的块级框会一个一个垂直摆放的。通过声明display:block,任何元素都能生成一个块级框。
  • 行内框(Inline box):这是一个由诸如strong,span之类元素生成的框。这些框不会在它之前、之后生成“换行”。通过声明display:inline,任何元素都能生成一个行内框。
  • 行内-块框(Inline-block box):这是一个内部类似于块级框,但外部类似于行内框的框。它的行为与可替换元素相似,但不完全相同。想象一下,把一个div粘贴到一行文本中,就好像它是嵌入图像一样。

包含块(the containing block)

  • 每一个元素的框都是相对于其包含块进行布局;实际上,包含块是盒子/框的“布局上下文”。
  • 对于正常的西式文本流中的元素,包含块是从生成列表项或块级框(list item or block box)最近祖先内容边缘开始形成的。
  • html元素所在的包含块是初始包含块(initial containing block)

改变元素显示方式

通过给dispaly属性设置显示的值来改变样式的显示方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
Values:         [<display-outside> || <display-inside>] | <display-listitem> |
<display-internal> | <display-box> |<display-legacy>
Initial value: inline
Inherited: No
Animatable: No

<display-outside>: block | inline | run-in
<display-inside>: flow | flow-root | table | flex | grid |ruby
<display-listitem>: list-item
<display-internal>: table-row-group | table-column-group | table-row-group | ...
<display-box>: contents | none
<display-legacy>: inline-block | inline-list-item | inline-table |
inline-flex | inline-grid

使用display属性只是改变元素的显示角色,而不是改变它们的固有性质。内联元素可以是块元素的后代,但反过来不行。

1
2
3
<span style="display: block;">
<p style="display: inline;">this is wrong!</p>
</span>

块级框(Block Box)

块级框的行为有时是可预测的,有时是令人惊讶的。比如,沿水平轴和垂直轴放置框的处理可能不一样。详细如下图(盒模型):

盒模型

  • box-sizing
    box-sizing属性可以改变上述盒模型的widthheight值的实际包含的区域。默认的区域是(内容框)content-box
    box-sizing属性,适用于”接受宽度和高度值的所有元素”,所以它也适用于替换的行内元素(如img)和行内块元素。
    1
    2
    3
    4
    5
    Values:           content-box | padding-box | border-box
    Initial value: content-box
    Applies to: All elements that accept width or height values
    Inherited: No
    Animatable; No

水平格式化

  • box-sizing的默认值是content-box,则宽度的指定值将影响内容区域的宽度,而不是整个可见元素框的宽度。
  • 正常流中块级框的水平宽度的总和等于包含块的宽度。

水平属性

  • 有 7 个属性,margin-left,border-left,padding-left,width,padding-right,border-right,margin-right.
  • 这 7 个属性的值相加等于该元素包含块宽度,即块元素的父元素的width值。
  • 3 个属性,width,margin-left,margin-right可以设置为auto,其余属性必须设置为特定值或默认的 0.

使用auto

总之上述 7 个属性相加等于包含块的宽度

  • 1 个 auto: auto = 父元素宽度-其余6个属性值之和
  • 2 个 auto: margin-left,margin-righautoauto: (父元素宽度 - width) / 2; width,其中1个外边距auto,则外边距减为0width = 父元素宽度 - 给定的外边距的值
  • 3 个 auto: 2 个外边距为 0.

负值 外边距

请记住,7 个水平属性的总和总是等于父元素的宽度。

1
2
3
4
5
6
7
8
9
div {
width: 500px;
border: 3px solid black;
}
p.wide {
margin-left: 10px;
width: auto;
margin-right: -50px;
}

10px + 0 + auto + 0 + 0 - 50px = 500pxauto = 540px, 则width:540px;

1
2
3
4
5
6
7
8
9
10
div {
width: 500px;
border: 5px solid black;
}
p.wide {
margin-left: 10px;
width: 600px;
margin-right: auto;
border: 3px solid gray;
}

10px + 3px + 0 + 600px + 0 + 3px + auto = 500px,auto = -116px, 则margin-right: -116px.

请记住:填充(padding)、边框(border)、内容宽/高(width/height)不可能是负值。只有外边距(margin)可以是负值。

百分比

  • 边框(border)的值只接受长度值,不接受百分比值。

  • 百分比值是相对于包含块(containing block)的宽度值(也就是其父元素的宽度)

    1
    2
    3
    4
    5
    6
    7
    8
    <p style="width: 67%; padding-right: 5%; padding-left: 5%; margin-right: auto; margin-left: 5%">
    playing percentages
    </p>

    <!-- 5% + 5% + 67% + 5% + auto = containing block width

    auto: 18%
    -->

替换元素(Relpaced Elements)

到目前为止,我们在处理正常文本流中不可替换的块框的水平格式。

  • 块级替换元素也使用于以上规则。
  • 块级替换元素如果width:auto,那么元素的宽度就是内容的固有宽度(例如,img元素,原始图像的宽度)。
  • img元素,width属性可以指定不同的宽度值。
  • 当可替换元素的宽度从固有宽度更改时,其高度值也会等比例的缩放以匹配,除非高度也被设置。同理,高度也是如此。
    1
    2
    3
    <img src="smile.svg" style="display: block; width: 25px; margin: 0;">
    <img src="smile.svg" style="display: block; width: 50px; margin: 0;">
    <img src="smile.svg" style="display: block; width: 100px; margin: 0;">

垂直格式化(Vertical Formatting)

  • 元素的内容决定了元素的默认高度。内容的宽度也影响高度,一个块级元素宽度越小,为了包含所有的内联内容,它就必须越高。
  • 当元素的内容比其块框的高度高时,其实际行为取决于属性overflow的值。
  • width一样,height默认定义内容区域的高度,而不是可视元素框(visible element box)的高度,除非box-sizing的值不是content-box.

垂直 相关 属性(Vertical Properties)

  • 同水平格式化一样,垂直相关的属性也涉及 7 个属性:margin-top,border-top,padding-top,height,padding-bottom,padding-border,margin-top.
  • 上述 7 个属性的值的和必须等于其包含块的高度,通常是其父元素的高度值。
  • height,margin-top,margin-bottom3 个值可以设置为auto.padding,border相关的值必须设置具体的值或者默认值为 0。
  • margin-top,margin-bottom在正常流中块框中设置为 auto,二者都自动重设为 0.0 值会妨碍正常流盒在其包含块中的垂直居中。

百分比高度

  • 正常流块框的高度设置成百分比,此值相对于包含块高度的。
  • 子元素的百分比高度是根据父元素的高度来确定的,当父元素的高度为不确定值时,或者父元素高度未定义时,子元素的高度百分比将无用,将被重设为 auto
    1
    2
    3
     <div style="height: auto;">
    <p style="height: 50%">Not Half as tall, height reset to auto</p>
    </div>

auto 高度

  • 如果一个正常流 块框设置height:auto,那么其高度值刚好可以将其内联内容的行框包裹起来。
  • 如果一个正常流 块框设置height:auto,并且仅有块级的子元素,则其高度值等于最顶层的子元素的顶部外边框边缘的到最底层的子元素的底部外边框边缘的距离。

垂直边距 塌陷/折叠/重叠(Collapsing Vertical Margins)

  • 垂直方向上相邻的 margin 会发生折叠行为。而paddingborder不存在折叠行为。
  • margin 发生折叠/重叠(collapse / overlap)行为,较小的边距被较大的边距所取代。
  • 在一个包含块中引入边框(border)或填充(padding)会导致其子元素的边距(margin)被包含在其中。 the introduction of a border or padding on a containing blokc would cause the margins of its child elements to be contained within it.

负边距和折叠(Negative Margins and Collapsing)

如果设置了负的垂直边距,则折叠后的边距为正、负边距相加的值。

1
2
3
4
5
6
7
8
9
10
11
p.neg {margin-top: -50px; margin-right: 10px;
margin-left: 10px; margin-bottom: 0;
border: 3px solid gray;}

<div style="width: 420px; background-color: silver; padding: 10px;
margin-top: 50px; border: 1px solid;">
<p class="neg">
A paragraph.
</p>
A div.
</div>

上述示例,p段落,被负上边距向上拉了 50px,它后面的div内容也被向上拉了 50px。
事实上,p段落后面的每一个正常流内容都被向上拉了 50px。 当div中的内容都被向上拉出,其高度为 0

行内元素

行 布局(Line Layout)

  • 为了理解如何生成行,首先要知道一个元素都包含一个很长的文本行。
  • text-align: justify时,每一都必须与段落的内容区域一样宽,使得该行的边缘与段落的内容边缘相接触。行长度和段落之间的差异通过改变每行中字母和单词之间的间距来弥补。所以word-spacingtext-align:justify覆盖,如果letter-spacing的值不是长度值也会被覆盖。

基本术语和概念

匿名文本(Anonymous text)

所有未包含在行内元素的字符串。例如<p>I'm <em>so</em> happy!</p>,”I’m “和” happy!”是匿名文本。注意,空格也是匿名文本,因为空格和其他字符一样都是正常的字符。

字符框(Em box)

em 框是在字体中定义的,也称字符框。实际的字形(glyphs)可能比 em 框更高或者更矮。在 CSS 中,font-size的值决定了每个 em 框的高度。

内容区(Content area)

在非替换元素中,内容区有 2 中,CSS 规范允许选择其中任意一种。内容区可以是元素中各字符的 em 框串在一起构成的框,也可以是有元素中字符字形描述的框。本书中,简单起见采用 em 框定义的。在替换元素中,内容区就是元素的固有高度+padding+border+margin

行间距(Leading)

行间距是font-sizeline-height值之差。这个差被一分为二,分别应用到内容区的顶部和底部。内容区增加的这两部分分别称“半间距(half-leading)”。行间距只应用于非替换元素

行内框(inline box)

这个框通过向内容区添加行间距来描述的。对应非替换元素,行内框的高度刚好等于line-height的值。对应可替换元素,行内框的高度刚好等于内容区高度,因为行间距不能应用于可替换元素。

行框(Line box)

这是包含该行出现的行内框的最高点和最低的的最小框。换句话就,行框的上边界要位于最高行内框的上边界,而行框的下边界要位于最低行内框的下边界。

根据前面介绍的术语和定义,CSS 还提供了一组行为和有用的概念:

  • 内容区类似于一个块级元素的内容框。
  • 行内元素的背景应用于内容区+padding.
  • 行内元素的边框包围着内容区以及任何 padding 和 border。
  • 非替换元素的 padding、border、margin 对行内元素或其生成的框没有垂直影响。也就是说,它们不会影响元素行内框的高度,因此也就不会影响包含元素的行框高度。
  • 替换元素的 margin、border 会影响该元素行内框的高度,从而也影响到包含该元素的行框的高度。
    需要注意,行内框根据vertical-align属性值 在行中垂直对齐

逐步构建一个行框?

如何逐步构建一个行框,通过此过程了解一行的各个部分是如何组合在一起,共同确定其高度的?
通过以下步骤确定行内每个元素的行内框的高度:

    1. 得到各行内非替换元素以及不属于后代行内元素的文本font-sizeline-height的值。line-height减去font-size,得到框的行间距。行间距 一分为二,将其分别应用到 em 框的顶部和底部。
    1. 得到各个非替换元素的heihgt,margin-top,margin-bottom,padding-top, paddding-bottom,border-top-width,border-bottom-width的值,并把它们加起来。
    1. 对于各内容区,确定有多少内容在整行基线之上和之下。这各任务并不容易:你必须知道每个元素和匿名文本段的基线的位置以及该行本身的基线位置,然后把它们对齐。另外,对于替换元素,要将其底部放在整个行的基线上
    1. 对于指定了vertical-align的元素,要确定其垂直偏移量。它将告诉你元素的行内框向上或向下移动了多远,并且它将改变元素在基线之上或之下多少。
    1. 现在已经知道所有行内框的位置,接下来 计算最终的行框高度。为此,只需要将基线与最高行内框顶部之间的距离 与 基线与最低行内框底部之间的距离 相加即可。

行内格式化(Inline Formatting)

  • 所有元素都有line-height,不管是否显示声明。这个值极大地影响了行内元素的的显示方式。
  • 一行的高度(或行框的高度)是由其组成元素和其他内容(如文本)的高度决定的。
  • line-height实际上只影响行内元素和其他行内内容,而不影响块级元素,至少不是直接影响。
  • 可以给一个块级元素设置line-height,并将该值用于到块中的所有内容,不论内容是否包含在行内元素中。但是只有在块级元素中有内联内容是才会由视觉效果。
    1
    <p style="line-height: 2em;"></p>
    由于没有内容去创建一个行框,段落也没有任何显示。
  • 从某种意义上块级元素中包含的每一行文本都是行内元素,而不乱 它是否被标签包围其来。如果你喜欢,可以想象一个虚构的标签序列 ,如下:
    1
    2
    3
    4
    5
    <p>
    <line>This is a paragraph with a number of</line>
    <line>lines of text which make up the </line>
    <line>conents.</line>
    </p>
    尽管line标签实际并不存在,但段落行为就像它们存在一样。每一行文都从段落继承了其样式。所以,只需给块级元素创建line-height规则,就不必显示的为它们的所以行内元素声明line-height.

行内非替换元素(Inline Nonrepalced Elements)

构建只包含 不可替换元素(或匿名文本)的行。这样可以很好的理解行内布局中不可替换元素和可替换元素之间的区别。

建立框 (Building the Boxes)

对于行内非替换元素或匿名文本某一部分,font-size的值确定了内容区的高度。line-height确定了行内框的高度。

1
2
3
4
5
6
<p style="font-size: 12px; line-height: 12px;">
This is text, <em>some of which is emphasized</em>, plus other text<br>
which is <strong style="font-size: 24px;">strongly emphasized</strong>
and which is<br>
larger than the surrounding text.
</p>

上例中,大多数文本font-size都是 12px,只有一个行内非 替换元素font-size是 24px。因为line-heihgt是继承属性,所以strong元素的line-height也是 12px,则其行内框高度为 12px,即 12px 高的行内框在元素内容区(24px)中垂直居中,行内框实际小于内容区。
到目前为至,似乎我们对每个文本都做了相同的处理,所有的行内框大小相同。但这并不完全正确。因为文本都是按基线对齐的,所以第二行虽然行内框大小相同,但排列的并不整齐。
由于行内框的顶部位于元素的内容区内,因此元素的内容溢出到行框之外,并于其他行框重叠。

垂直对齐(Vertical Alignment)

如果改变行内框的垂直对齐方式,会应用相同的高度确定原则。假设上例strong元素指定垂直对齐为 4px:

1
2
3
4
5
6
<p style="font-size: 12px; line-height: 12px;">
This is text, <em>some of which is emphasized</em>, plus other text<br>
which is <strong style="font-size: 24px; vertical-align: 4px;">strongly emphasized</strong>
and which is<br>
larger than the surrounding text.
</p>

此小改动会把strong元素上升 4px,会同时提升内容区和行内框。由于strong元素的行内框顶端已经是行中的最好点,垂直对齐这个修改会把整个行框的顶端也上移 4px
vertical-align各个关键字值的效果描述如下:

  • top, 将元素行内框顶部与包含它的行框顶部对齐
  • bottom, 将元素行内框底部与包含它的行框底部对齐
  • text-top, 将元素行内框与父元素的内容区顶部对齐
  • text-bottom, 将元素行内框与父元素的内容区底部对齐
  • middle, 将元素行内框的垂直中点与父元素基线上 0.5ex 处对齐
  • super,将元素行内框和和内容区上移。具体距离,不同浏览器各部相同
  • sub,同super相同,元素行内框和内容区下移。
  • <percentage>,将元素上移或下移一定的距离,这个距离通过元素line-height值的声明百分比来确定。
管理行高(Managing line-height)

改变一个行内元素的line-height可能导致文本行相互重叠,不过,在所有情况下,这种修改都是针对单个元素的。所有如何以一种更普遍的方式影响元素的line-heght而避免内容重叠呢?

  • font-size有改变的元素结合使用em单位。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    p {
    font-size: 14px;
    line-height: 1em;
    }
    p big {
    font-size: 250%;
    line-height: 1em;
    }

    <p>
    Not only does this paragraph have "normal" text, but it also<br>
    contains a line in which <big>some big text</big> is found.<br>
    This large text helps illustrate our point.
    </p>

    通过给big元素设置line-height,就提高了行框的总高度,从而提供足够的空间来显示big元素。
    line-height中的em是相对于元素本身的font-size设置的,而不是相对于父元素设置

  • line-height的值作为缩放因子,且是一个继承值而非计算值。它会在元素间逐层传递,在各层上,这个因子与各元素的font-size相乘,结果就是各元素的line-height.

    1
    2
    3
    4
    5
    6
    7
    p {
    font-size: 14px;
    line-height: 1.2;
    }
    big {
    font-size: 250%;
    }
    • 如果在增加边框,问题会更复杂,只需要足够大的line-height来容纳这个边框即可。
基线与行高

每个行框的实际高度取决于其组成元素彼此之间如何对齐。这种对齐方式很大程度上依赖于基线在每个元素(或匿名文本)中的位置。,因为这个位置决定; 行内框的如何摆放。每个 em 框中基线的位置对于每种字体都是不同的。这各信息内置在字体文件中,除非直接编辑字体文件,否则不能通过任何方式更改。

增加边框属性(Adding Box Properties)
  • padding,margin,border都可以应用于行内非替换元素,但这些方面不会影响到行内元素的行框高度。
  • 行内元素的border边界由font-size控制,而不是line-height。换句话就,如果一个span元素font-size是 12px,line-height是 36px,其内容区就是 12px,border将包围这个内容区。
  • 可以为行内元素指定padding,这会使border远离文本本身。注意,这个padding并没有改变内容区高度的具体形状,因此它也不会影响这个元素的行内框的高度,类似的,向行内元素添加border也不会影响行框的生成和布局
  • 至于margin,实际上,它不应用于行内非替换元素的顶部和底部,也就不影响行框的高度,但是会影响行内元素的两端。
  • 行内元素基本上会作为单行布局的,然后被分解成多个部分。所有,向一行内元素应用margin,这些margin只会出现在其开始和末尾,分别是margin-left,margin-rightpadding也是如此。尽管padding,margin不影响行高,但是它们仍然能 影响一个元素的布局,通过将文本推、离其左右两端。实际上,负的margin,可能会把文本拉近,甚至导致重叠
  • CSS2.1 明确指出行框按文档属性绘制,“这会导致后续行的边框在前面行的边框和我文本上绘制/覆盖”
字形和内容区(Glyphs, Content Area)

一个字体的 em 框与其字符字形之间可能存在差别,对于大多数字体,其 em 框的高度与字符字形的高度都不一致。
一个行内非替换元素的“绘制区”由用户代理(浏览器)决定。如果一个用户代理使用 em 框的高度作为内容区的高度,那么行内非替换元素的背景就与 em 框的高度相同(即 font-size 的值)。如果用户代理使用字体的最大上升变形和下降变形,背景就可能比 em 框高或低。

行内替换元素
  • 一般认为行内替换元素(如 img)有固有的高度和宽度。它可能导致行框比正常的要高,但这不会改变行中的任何元素的line-height值,包括替换元素本身。相反,只会让行框的高度恰好能包含替换元素(已经任何框属性)。换句话就,会用替换元素的整体(包括内容、padding、border、margin)来定义元素的行内框
  • 行内替换元素有一个line-height的值,在垂直对齐时需要它来正确地地位元素,vertical-aline 的百分比值要相对于元素的line-height来计算的
增加框属性
  • paddingborder像平常一眼样应用到行内替换元素:padding在具体内容外插入空间,border围绕着padding。**padding,border确实会影响行框的高度,因为它们是行内替换元素的行内框的一部分。**
  • margin也包含在行框中,正margin使替换元素的行内框更高。负margin会减少行内框的大小,使行内替换元素挤入其他行
替换元素和基线(Relpaced Elements and the Baseline)
  • 替换元素并没有自己的基线,相对来讲最好的办法就是将其行内框的底部与基线对齐,实际上,就是下外边距的边界与基线对齐
    Inline replaced element sit on the baseline
  • 只要一个行内替换元素是 块级元素或表单元格元素中的唯一后代,替换元素都会生成行框,并且替换元素(如图像)放在基线上
  • margin 会使行内替换元素向下拉
行内块元素(Inline-Block Elements)
  • 行内块元素作为一个行内框与其他元素和内容相关,它就像一个图像一样放在一个文本行中,实际上,行内块元素会作为替换元素放在行中。这就意味着,行内块元素的底端默认地位于文本行的基线上,而且内部没有行分隔符。
  • 在行内块元素的内部,其内容像块级元素一样被设置。就像所有块级元素和行内替换元素一样,行内块元素也有width,heihgt属性,如果其比周围的内容高,这些属性会使行高增加。
  • 如果行内块元素的width未定义,或显式声明未auto,元素框将收缩以适应内容,即元素框的宽度刚好足够包含该内容,没有多余空间。行内框也是如此,不过行内框可以跨多个文本行(同其他行内容覆盖),但行内块元素不会
Flow Display
Contents Display
计算值(Computed Values)

如果元素是浮动元素或定位元素,display的计算值会改变。实际上,display,position,float值会相互影响。

小结(Summary)

display属性指定了元素的显示类型,包含两类基础特征:

  • 外部显示类型:用于指定元素怎样生成盒模型,定义了元素怎样参与流式布局/文档流的处理。例如block,inline,run-in
  • 内部显示类型:定义了元素内子元素的布局方式。例如flow,table,flex,grid