其他语言的处理
其实在浏览器中,一共支持四种语言。
针对JS
的处理,需要用到V8
等引擎,WebAssembly
也是需要做处理的。因为,篇幅有限,这里就不展开描述了。
针对JS
的解析过程,可以参考JS执行流程
关于WebAssembly
的介绍,可以参考浏览器第四种语言-WebAssembly
{布局|Layout}阶段生成 {不可变fragment树|immutable fragment tree}
在构建完DOM并计算所有样式后,下一步是确定所有元素的视觉几何属性。
对于块级元素
,我们需要计算一个矩形的坐标
,该矩形对应于元素所占据的内容的几何区域。
块元素 和 内联元素
对于前端页面元素而言,一个元素的类型可以隶属于不同的类型。但是,在比较宏观的角度看,元素是否占一行还是可以和文本信息同行显示。可以把元素分成块元素和内联元素。
块元素
在最简单的情况下,布局按照DOM的顺序,从上到下,依次放置。我们称之为block flow。(单独占一行)
内联元素
文本节点和类似<span>
的内联元素生成{内联框|inline boxes},通常在一行中从左到右流动。
而从右到左
的内联流动方向则适用于RTL语言
,如阿拉伯语
和希伯来语
。
确定字型的大小和位置
{布局|Layout}需要使用ComputedStyle
对象中的{字体|font}信息来测量文本。 (这里再重申一下,ComputedStyle
是CSS被解析后的对象)
{布局|Layout}使用名为
HarfBuzz
的文本整形库来计算每个字形的大小和位置,从而确定文本段的整体宽度。
矩形边界
{布局|Layout}可能会为单个元素计算多种类型的矩形边界。
例如,在出现溢出情况时,布局会计算{边框框盒|border box rect}和{布局溢出框盒|layout overflow rect}。
如果节点的溢出是可滚动的,布局还会计算{滚动边界|scroll boundaries}并保留滚动条的空间。
最常见的可滚动DOM节点是文档本身,它是树的根节点。
布局对象的内容可以超出其{边框框盒|border box rect}。
同时,我们还可以设置如何处理超出部分的行为。
overflow:'auto'|'visible'|'hidden';
(是不是很熟悉)
其他复杂的布局情形
对于表格元素
或指定将内容分成多列的样式,或者浮动对象使内容围绕其一侧流动,需要更复杂的布局。
但是,不管布局如何复杂,在布局阶段,有一个亘古不变的规则就是: DOM结构和计算样式值(ComputedStyle
)是{布局|Layout}算法的输入
每个流水线阶段都使用前一个阶段的结果。
布局行为不同,有不同的布局对象
{布局|Layout}在与DOM链接的单独树
(布局树)上进行操作。(也就是说DOM树
和Layout树
有关联,但是不是一个树)
{布局树|Layout Tree}中的节点实现了布局算法。
根据所需的布局行为,有不同的LayoutObject
子类。样式更新阶段
也会构建布局树。
布局阶段遍历布局树,对每个
LayoutObject
执行布局操作。
DOM 节点和布局对象不是一对一的关系
通常情况下,一个DOM节点对应一个LayoutObject
。但有时候,一个LayoutObject
没有对应的DOM节点。
甚至有可能一个节点有多个LayoutObject
(例如,一个内联元素在块级子元素内,并且内联元素之前和之后都有文本)。可以参考下图中<span>inline</span>
的布局对象。
最后,布局树的构建基于FlatTreeTraversal
(FlatTreeTraversal
在解析DOM的时候,当存在多个DOM树的时候,出现过哈),可以跨越影子DOM边界。
NG 布局引擎
布局引擎正在进行重写。布局树包含了传统布局对象
和NG布局对象
的混合。最终,所有的布局对象将会是NG布局对象。
在NG中,布局的输入和输出被清晰地分离开来。输出是一个不可变的、可缓存的布局结果。
{NG布局结果|NGLayoutResult}指向描述物理几何结构的{片段树|Fragments Tree}。
实践验证
存在如下的页面结构。
<div style="max-width: 100px"> <div style="float: left; padding: 1ex">F</div> <br>The <b>quick brown</b> fox <div style="margin: -60px 0 0 80px">jumps</div> </div>
最后的呈现效果。
生成DOM树
生成Layout树
{布局对象|Layout Object}大多数情况下与DOM元素一对一对应。
但是,在Layout
树中也会存在anonymous
布局对象,它是为了使其容器只包含块级子元素而创建的。
{布局块|LayoutBlock}可以具有块级子元素或内联子元素,但不能同时具有两者。
虽然,文本"quick brown" 存在断行情况,但是它是存在一个LayoutText
节点中。
生成不可变Fragment树
在{片段树|Fragment Tree}中,我们可以看到断行的结果以及每个片段的位置和大小。
片段的断行的结果
片段位置和大小
{绘制|Paint}阶段生成{显示列表|Display List}
通过上述的数据处理,我们已经获取到{布局对象|Layout Object}的几何属性,接下来我们就需要将其绘制处理了。
{绘制记录|Paint Records}将绘制操作记录到{显示项|Display Items} 列表中。
绘制操作可以是诸如"在这些坐标上以这种颜色绘制一个矩形"之类的内容。
对于每个{布局对象|Layout Object}可能会有多个{显示项|Display Items},对应着其不同的视觉呈现部分,如背景、前景、轮廓等等。
按照层叠顺序进行元素绘制
层叠顺序
这里多啰嗦几句,在CSS重点概念精讲中我们介绍过,关于层叠上下文和层叠顺序,这里我们只是做简单的知识介绍,如果想了解更多,可以参考之前的文章。
{层叠顺序|Stacking Order}表示元素发生层叠时有着特定的垂直显示顺序
一旦普通元素具有层叠上下文
,其层叠顺序就会变高
分两种情况
- 如果层叠上下文元素不依赖
z-index
数值,则其层叠顺序是z-index:auto
- 可看成
z-index:0
- 如果层叠上下文元素依赖
z-index
数值,则其层叠顺序由z-index
值决定
按照层叠顺序进行页面绘制
按正确的顺序绘制元素非常重要,这样它们在重叠时才能正确叠放。绘制顺序可以通过样式来控制。
绘制顺序是按照层叠顺序,而不是DOM顺序
可以看到,虽然yellow
的DOM顺序在green
的DOM之前,但是在绘制到页面上时,yellow
在green
的上面。(yellow
Z轴大)
每个绘制过程都是对层叠上下文的单独遍历
甚至有可能一个元素部分在另一个元素前面,部分在后面。这是因为绘制过程分为多个阶段,每个绘制阶段都会对子树单独遍历。
存在如下的页面结构:
<section class="container"> <div id="green"></div> <div id="blue"></div> </section>
对应的样式如下:
#green { position: relative; background-color: green; height:200px; width:300px; } #blue { position: absolute; top: 20px; left: 30px; width: 200px; height: 100px; background-color: blue; border: 5px solid black; } #green::before { content: "绿色元素的文案信息"; position: absolute; top: 20px; left: 10px; z-index: 2; color:white; font-weight: 700; }
在不考虑,CSS3
其他特殊属性的情况下,当元素设置了z-index
,就会生成一个层叠上下文,并且每个绘制阶段都是对层叠上下文的单独遍历。
实践验证
存在如下的页面结果。
<style> #p { position: absolute; padding: 2px; width: 50px; height: 20px; left: 25px; top: 25px; border: 4px solid purple; background-color: lightgrey; } </style> <div id=p> pixels </div>
呈现的效果如下: