提炼css-display
这个module
中的一些要点。
css box tree
css
把一个包含elements
和text nodes
的document tree
渲染到canvas
上。css
生成了一个中间结构:box tree
,表示document tree
的格式化渲染结构。box tree
上的每个box
与相应的element or pseudo element
对应,box tree
上的每个text run
与text nodes
的content
对应。
对每个element
而言,css
会根据它的display
属性值为它自身创建0到多个box
。 一个element
至少会有一个box
,称为principal box
,表示element
自己以及它在box tree
中包含的内容。 有一些display
值会生成多个box
。比如list-item
这个值,会生成一个principal block box
和一个marker box
。有一些值比如none or contents
不会生成任何box
。box
可以用display
的类型来指代,比如一个display:block
的element
可以被称为一个block box
或者block
。
除非另有说明,box
会被设定与它对应的element
相同的样式。inherited properties
(可继承的属性)被设定到element
的principal box
上,然后通过box tree
的层级关系继承给该element
生成的后代box
。non-inherited properties
(不可继承属性)默认情况下也是应用到principal box
上,但是如果一个element
自身要生成多个box,就可能把non-inherited properties
应用到其它box
,而不是principal box
。比如table
元素的border
属性,是一个non-inherited property
,它是应用到table grid box
上的,不是table wrapper box
。补充:table
的princial box
又称为table wrapper box
。
document tree
中连续的text nodes
,css
会为它们生成一个text run
的东西,来包含这些文本内容,每个text run
会被设定成与它们对应的text nodes
相同的样式。
在创建box tree
的过程中,某一个element
生成的boxes
(应该指所有)都是这个元素所有祖先elements
的principal box
的后代。通常来说,一个元素的principal box
的direct parent box
就是这个元素祖先元素中,离它最近并且有生成box
的那个祖先元素的principal box
;但是有例外,比如下面的anonymous box
的情况。补充理解:direct parent box
的含义,也可以理解为一个元素的principal box
的direct parent box
,大部分情况应该就是它父级元素的principal box
。
anonymous box
就是与任何element
都没有关系的box
。anonymous box
都是在特定情况下才会生成用来修复box tree
结构;这个特殊情况是指box tree
需要一个特别的box
嵌套结构,但是这个嵌套结构没有被element tree
提供。 比如,一个table cell box
要求它的parent box
必须是一个tabel row box
,所以如果一个display:table-cell
的element
父级,并不是一个display: table-row
的element
时,那此时element tree
就没有满足table row box -> table cell box
这种嵌套结构,css
会自动在table cell box
的外层生成一个table row box
来作为它的parent box
。
在布局过程中,box
和text run
可能会被分割为多个fragments
。比如,当一个inline box
或者是text run
因为换行,就会被分割为多个fragments
;一个block box
也会因为page
(分页)和columns
(分栏),被分割为多个fragments
;这个分割过程叫做fragmentation
。因此一个box
可能会存在0到多个box fragments
,一个text run
可能存在0到多个text fragments
。
display property
display
定义了element
的display type
,包含两个方面:
inner display type
决定它自己生成一个什么类型的formatting context
,控制由它生成的descendant boxes
如何布局;outer display type
决定element
自己的principal box
如何参与flow layout
。
注意此要点的描述:outer display type
决定的是自己如何参与flow layout
,而css
中并不只有一种layout
,除了flow layout
,还有flex layout
、table layout
等,那么当一个元素位于一个flow layout
当中,outer display type
才有意义,否则没有意义。比如当元素位于flex layout
或table layout
当中,outer display type
就没有意义。
value
display
的value
定义:1
[ <display-outside> || <display-inside> ] | <display-listitem> | <display-internal> | <display-box> | <display-legacy>
各个value component
的定义:1
2
3
4
5
6
7
8
9
10<display-outside> = block | inline | run-in
<display-inside> = flow | flow-root | table | flex | grid | ruby
<display-listitem> = <display-outside>? && [ flow | flow-root ]? && list-item
<display-internal> = table-row-group | table-header-group |
table-footer-group | table-row | table-cell |
table-column-group | table-column | table-caption |
ruby-base | ruby-text | ruby-base-container |
ruby-text-container
<display-box> = contents | none
<display-legacy> = inline-block | inline-table | inline-flex | inline-grid
根据以上语法说明,以下的value
形式都是允许的:1
2
3
4
5
6
7
8display: <display-outside>;
display: <display-inside>;
display: <display-outside> <display-inside>;
display: <display-inside> <display-outside>;
display: <display-listitem>;
display: <display-internal>;
display: <display-box>;
display: <display-legacy>;
特殊的display-listitem
还会出现下面这种奇怪的display
值写法:1
2
3display: block flow list-item;
display: list-item;
display: block list-item;
以上value type
涉及到的<> || | []
以及下面的&& ?
等符号的语法,是css
在规范文档中用来描述Value Definition
的一种语法,可前往css-values这个文档学习。
综合以上内容可以看到,display
的值是支持单值、双值和三值写法的:1
2
3display: block flow;
display: block flow list-item;
display: inline-block;
不过目前这个写法还没有得到支持,但是可以从这个角度去理解display
,因为多值的写法能够清晰地看到inner display type
和outer display type
,大部分单值写法只是多值写法的简写形式。
display-outside
<display-outside>
这个value type
,决定了display
的outer display type
,它包含3个值:1
<display-outside> = block | inline | run-in
其中run-in
并没有完全支持,所以暂不讨论。另外两个含义如下:
block
表示当元素处于flow-layout
当中时,生成的box
是block-level
的inline
表示当元素处于flow-layout
当中时,生成的box
是inline-level
的
要分析一个display
值的outer display type
只要找到display
值里面跟<display-outside>
对应的部分即可。当讨论一个box
是block-level
还是inline-level
,说的是box
的outer display type
。
display-inside
<display-inside>
这个value type
,决定了display
的inner display type
,包含以下几个值1
<display-inside> = flow | flow-root | table | flex | grid | ruby
其中ruby
并没有完全支持,所以暂不讨论。其它值的含义如下:
flow
表示该元素的包含的内容,会使用flow layout
进行布局,flow layout
就是block-and-inline layout
,它包含两种formatting context
:block formatting context
和inline formatting context
。在display-insde: flow
的前提下,当outer display type
是inline
时,生成的box
是一个inline box
;当outer display type
是block
,生成的box
是一个block-level
的box
,称为block container
。block-container
与block box
不完全相同,block box
是突出一个box
是block-level
的,而block-container
是突出一个box
的能力,见下面对block container
的解释。当一个box
是inline box
的时候,它始终带着自己和自己包含的内容参与到一个inline formatting context
当中一起布局;当一个box
是一个block container
的时候,它要么会新建一个BFC
来布局子内容,要么把子内容放到自己所在的那个block formatting context
中一起布局。flow-root
这个跟flow
唯一的区别就是,它一定会让box
新建一个BFC
。那种新建了BFC
的block container
都可以当成inner display type
为flow root
来看待。table
表示元素会生成一个principal box
,也叫做table wrapper box
,并且新建一个block formatting context
,在此context
内再生成一个table grid box
,并且使用一个新的布局context
:table formatting context
来布局内容,这就是所谓的table layout
。table wrapper box
应该是一个block container
,因为它会创建BFC
。flex
表示元素会生成一个flex container box
,并创建一个新的布局context
:flex formatting context
,使用flex layout
来布局内容。这个flex container
跟block container
是完全不同的,flex container
指的是这个box
是1个flex formatting context
的根。grid
表示元素会生成一个grid container box
,并创建一个新的布局context
:grid formatting context
,使用grid layout
来布局内容。这个grid container
跟block container
是完全不同的,grid container
指的是这个box
是1个grid formatting context
的根。
block container
block container box
要么只包含inline box
,要么只包含block box
。怎么做到这一点呢?需要一个box
只包含inline-box
比较容易,但是要它只包含block-box
如何做到?实际上还是借助anonymous box
,比如下面这个结构:1
2
3
4<div>
asd
<p>abc</p>
</div>
div内混合了文本和p
节点,css
会在asd
外面包裹一个anonymous block box
来保证div
这个block container
仅包含block box
。
当一个block container
只包含inline box
的时候,它会创建一个inline formatting context
,并且会在所有的inline box
外面包裹一层anonymous box
来作为这些inline content
的root inline box
。
block container
在它的parent formatting context
不是一个block formatting context
的时候,会创建一个新的block formatting context
来布局它的内容。这个点值得关注,下面这个例子能证明这个点是对的:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style type="text/css">
.parent {
width: 400px;
background-color: #ddd;
margin: 0 auto;
}
.child {
width: 100px;
height: 100px;
background-color: red;
float: left;
}
.main {
display: flex;
}
</style>
</head>
<body>
<div class="main">
<div class="parent">
<div class="child"></div>
</div>
</div>
</body>
</html>
例子中,div.main
是一个flex formatting context
,而div.parent
是一个block container
,所以div.parent
会创建一个BFC
,这样div.child
浮动带来的高度塌陷问题就没有了。
如果block container
的parent formatting context
是一个BFC
,那么block container
就是按两种方式来布局它包含的内容:第一种,还是新建一个BFC
,比如overflow float position
这些属性可能会导致新的BFC
被创建;第二种,就是将它的内容放到parent formatting context
中去布局。第二种方式,有一个场景值得说明:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style type="text/css">
.parent {
width: 400px;
background-color: #ddd;
margin: 0 auto;
}
.child {
width: 100px;
height: 100px;
background-color: red;
float: left;
}
</style>
</head>
<body>
<div class="parent">
<div>yes</div>
<div>yes</div>
<div>yes</div>
<div>yes</div>
<div>yes</div>
<div class="child"></div>
</div>
<p>surrounds</p>
</body>
</html>
上例中,为什么与div.parent
同级的p
元素会环绕在div.child
这个浮动元素的右边呢?这是因为上面的例子中div.parent
并没有创建新的BFC
,所以它的内容都是在body
的formatting context
中布局出来的,而div.child
虽然设置了浮动,但是浮动只能让它从文档流脱离出来,它仍然属于body
的block formatting context
,而div.parent
后面的p
也是在body
的context
中布局的,这样p
里面的文本所形成的inline formatting context
与浮动元素的作用关系,导致了这个结果。浮动本质上不会脱离元素所在的block formatting context
。如果想让p
与div.child
不产生浮动环绕的作用,解决办法就是把div.parent
变为BFC
,这样div.child
与p
不在同一个block formatting context
,它们是不会产生浮动环绕作用的。
block container
可以同时有两种formatting context
,比如它仅包含inline box
,这样它会创建inline formatting context
,然后再通过别的方式触发新建BFC
,这样就两种formatting context
共存了。
block container
vsblock box
:
block box
是指block-level box
,它参与flow layout
时一定是block-level
的,它不一定是block container
,比如display: block
的replace elements
所生成的box
以及display: flex
的元素生成的flex container
都是block box
,但不是block container
block container
也不一定都是block box
,比如display: inline-block
和display: table-cell
的元素生成box
会创建BFC,属于block container
,但不是block-level box
缺省说明
如果display
值,指定了<display-outside>
但是没有指定<display-inside>
,<display-inside>
的默认值就是:flow
。
如果display
值,指定了<display-inside>
但是没有指定<display-outside>
,<display-outside>
的默认值就是:block
。
display-listitem
这个value type
的定义是:1
<display-listitem> = <display-outside>? && [ flow | flow-root ]? && list-item
其中<display-outside>
和[flow | flow-root]
是可选的,list-item
这个关键词是必须有的。<display-listitem>
把inner display type
限制为flow | flow-root
,暂时没有其它的inner display type
。如果未指定outer display type
,则outer display type
默认值是:block
,所以li
元素默认都是block-level
的;如果未指定inner display type
,则inner display type
默认值是:flow
。 所以<display-listitem
>可定义的display
值的形式有:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15/* a. */
display: list-item;
/*以下3个与a.均等价*/
display: block flow list-item;
display: flow list-item;
display: block list-item;
/*以下两个等价*/
display: inline list-item;
display: inline flow list-item;
/*以下两个等价*/
display: flow-root list-item;
display: block flow-root list-item;
从上面的举例也能看到,display: list-item
这种单值写法是多值写法的简写形式。
display-listitem
相比<display-outside> || <display-inside>
,最大的区别其实是它会让element
多生成一个marker box
来表现项目列表
的符号,这就是为啥li
元素默认情况下,前面会有项目符号的原因。
display-legacy
这个value type
定义为:1
<display-legacy> = inline-block | inline-table | inline-flex | inline-grid
这几个关键词就是几个简写形式:
inline-block
等价于inline flow-root
,所以display:inline-block
的元素其实是个BFC
inline-table
等价于inline table
inline-flex
等价于inline flex
inlin-grid
等价于inline grid
display-internal
这个value type
的定义:1
2
3
4
5<display-internal> = table-row-group | table-header-group |
table-footer-group | table-row | table-cell |
table-column-group | table-column | table-caption |
ruby-base | ruby-text | ruby-base-container |
ruby-text-container
一些布局(例如table
ruby
)具有复杂的内部结构,其子代和后代可以担当几种不同的角色,display-internal
实际上就是在指定元素在table
ruby
这种特定布局里面充当的角色,所以display-internal
跟其它的display
的value type
不一样,它只有在特定的布局里面才有意义。而且非常特殊的是,display-internal
的值,除非另有说明,否则使用这些值的元素生成的box的inner display type
和outer display type
都将设置为给定关键字。比如display: table-row-group
,这个元素的inner display type
和outer display type
就都是table-row-group
。 所以前面好多的关于outer display type
和inner display type
的知识,在display-internal
不一致的,这是css
针对table
ruby
布局的内部处理。
不考虑ruby
,display-internal
这个值的详细含义如下:
table-row-group, table-header-group, table-footer-group, table-row, table-cell, table-column-group, table-column
表示这个元素是一个table
布局的子元素,它会相应的创建internal table box
来参与table layout
。特殊的是,table-cell
指定了inner display type
为:flow-root
,所以display:table-cell
的元素会创建BFC
。table-caption
表示这个元素会生成一个table caption box
,它是一个block box
,并且和table and table wrapper boxes
有特殊的行为,同时它还有指定inner display type
为:flow-root
,所以display:table-caption
的元素也会创建BFC
。
归纳总结
综合以上内容,发现:学习display
的值,主要抓这几个要素:
- 分析它生成的
box
,1个还是多个,每个box
的名称是啥 - 分析它的
inner display type
,遵循什么layout
,会不会新建formatting context
- 分析它的
outer display type
,看看是block-level
还是inline-level
下面是一个对常见display
值的归纳整理:
display:block
完整写法:
display: block flow
outer display type:block
inner display type:flow
box level:block-level
生成的box:一定是block box
不一定是block container
,见前面对此二者的区分说明
formatting context: 要么新建一个BFC
布局子内容,要么把子内容布局到自己所在的BFC
。display:flow-root
完整写法:
display: block flow-root
outer display type:block
inner display type:flow-root
box level:block-level
生成的box:block container
formatting context: 新建一个BFC
布局子内容display:inline
完整写法:
display: inline flow
outer display type:inline
inner display type:flow
box level:inline-level
生成的box:inline box
formatting context: 将子内容布局到自己所在的inline formatting context
display:inline-block
完整写法:
display: inline flow-root
outer display type:inline
inner display type:flow-root
box level:inline-level
生成的box:block container
formatting context: 新建一个BFC
布局子内容display:list-item
各个要素与前面四个几乎一致,就是会多生成一个
marker box
display:flex
完整写法:
display: block flex
outer display type:block
inner display type:flex
box level:block-level
生成的box:flex container
formatting context: 新建一个flex formatting context
布局子内容display:inline-flex
完整写法:
display: inline flex
outer display type:inline
inner display type:flex
box level:inline-level
生成的box:flext container
formatting context: 新建一个flex formatting context
布局子内容display:grid
完整写法:
display: block grid
outer display type:block
inner display type:grid
box level:block-level
生成的box:grid container
formatting context: 新建一个grid formatting context
布局子内容display:inline-grid
完整写法:
display: inline grid
outer display type:inline
inner display type:grid
box level:inline-level
生成的box:flext container
formatting context: 新建一个grid formatting context
布局子内容display:table
完整写法:
display: block table
outer display type:block
inner display type:table
box level:block-level
生成的box:table wrapper box
包含table grid box
,其中table wrapper box
是一个会新建BFC
的block-container
,且是block-level
的
formatting context:table wrapper box
新建BFC
,而table grid box
新建table formatting context
display:inline-table
完整写法:
display: inline table
outer display type:inlne
inner display type:table
box level:inline-level
生成的box:table wrapper box
包含table grid box
,其中table wrapper box
是一个会新建BFC
的block-container
,且是inline-level
的
formatting context:table wrapper box
新建BFC
,而table grid box
新建table formatting context
display type的自动转换
blockification
: 块级化,将box
的outer display type
强制设定为block
inlinification
: 内联化,将box
的outer display type
强制设定为inline
一些布局可能会对元素的box
进行blockification
或者是inlinification
的处理,比如浮动 或绝对定位 或flex布局
都会对元素进行blockification
。