目录概览:
- 深入理解HTML语义
- 让IE8支持HTML5语义化标签
- 视觉格式化模型
- 深入了解inline-block
- flexbox剩余空间分配规则
- grids布局系统
- 浏览器如何渲染HTML & CSS
- 重排与重绘
一、深入理解 HTML 语义
HTML 语义当然不仅仅只是几个 HTML 语义标签。
从“文档”说起
很多时候我会把“页面”和“文档”这两个词混着用,比如将 HTML 页面说成“HTML 文档”。
HTML 就是文档,最开始的《Web 简史》中我们有提到过,万维网的雏形是一个文档共享系统,万维网就是一个放大版的文档共享系统。
只是随着 Web 的发展,各种酷炫的页面和应用层出不穷,倒是让新入行的小伙伴忽略了,HTML 的本质其实是文档(document)。
现实生活中,说到文档,我们的第一反应就是 Microsoft Word ,其实 Word 当年也有可能成为 Web 标准,当然这是另外一个故事。
文档大纲
所以我们从 Word 说开去,希望你 Word 用的很顺溜哦。
Word 中的标题有一级标题、二级标题……的说法,所有的标题就构建出了这个 Word 文档的大纲。下图的左边栏就是大纲视图,可以很好的观察文档大纲。
在 HTML 文档中呢,H1 ~ H6 就是一级到六级标题,它们构成了 HTML 文档的大纲。下面是腾讯网首页的文档大纲。一个好的页面,必定先是一个好文档,好文档必然有着严谨的文档大纲。
这个是谷歌浏览器查看页面文档大纲的插件,html5-outliner 。
再来看“万维网”
最初的万维网是一个文档共享的网络,现在的万维网则是一个资源共享的网络,包括图片、多媒体等等。
HTML 则是万维网的粘合剂,也是万维网的载体,但是现在 HTML 给我们的感觉啊,就是给人看的,其实,HTML 同时也会给机器看,比如下图,除了 human 在读 HTML ,各式各样的机器也在读 HTML ,比如搜索引擎的爬虫和读屏设备。
视觉上的各种酷炫会给人以视觉冲击,但对机器来说,并没有什么用,它们更看重的是语义,这样才能更好地解析内容。这也是为什么样式会从结构里面分离出来的原因之一。
现在才到 HTML 语义
本文的开头就提到了,HTML 语义当然不仅仅只是几个 HTML 语义标签。在 HTML 本身这个层面上,语义也有更多的东西,关于这些已经有不少前辈为我们总结好了,参考如下:
- HTML5中最看重的理念“语义化”相比HTML有什么区别?
- HTML 语义
二、让 IE8 支持 HTML5 语义化标签
HTML5是 HTML 最新的修订版本,于2014年10月由万维网联盟(W3C)完成标准制定。而 IE8 面世时间为2009年3月19日,时间相差如此之大,所以 IE8 作为比较古老的浏览器,不支持 HTML 5 引入的语义化标签(如 header、nav、menu、section、article 等)也是很正常的。
默认情况下 IE8 对 HTML5 标签的处理
在 IE8 里面,未定义的标签——IE8 不认识所有新引入的 HTML5 标签,所以定义样式是不会生效。比如下面这段代码(抽取主要代码):
hello world
期待展示的效果如下:
但在 IE8 中实际展示效果如下:
如何让 IE8 支持 HTML5 标签
虽然默认不支持,但是我们可以通过 JS 使用 document.createElement
来“欺骗” IE 的 CSS 引擎,让它知道某个标签的存在,具体做法如下:
HTML5 test
Hello!
显示效果如下:
既然元素默认都不支持,就更没有相关默认的样式了,所以我们还要加上一些重置样式如下:
article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary {
display: block;
}
借助 html5shiv.js
让 IE8 支持更多的 HTML5 特性
其实不只是 IE8 , IE6-9、 Safari 4.x (以及 iPhone 3.x)、还有Firefox 3.x 等等,对 HTML5 的支持都不完善。所以有了一个库 html5shiv.js
来做统一处理,shiv 意为用作武器的小刀(实际上是一个拼写错误,应该为 shim),在机械工程中的专业释义为垫片,比喻给那些老旧的浏览器加个垫片,让它们基本能用。
更多阅读
- IE8 HTML5 surport
- HTML5 shiv
- html5shiv.js
- github上html5shiv项目readme.md部分的翻译
- HTML5 Shiv 的一些趣事 ,英文原文见 The Story of the HTML5 Shiv。
三、视觉格式化模型(visual formatting model)
前面我们已经学习了盒模型(box model),知道了元素会被渲染成一个个盒子。那么这些盒子在屏幕上的位置又是怎么放置的呢?这就是我们现在要学习的——CSS 视觉格式化模型(visual formatting model)。视觉格式化模型是 CSS 布局的一个基础理论体系,需要你有一定的 CSS 功底,所以一时半会是很难掌握的,但是只要你一掌握,对于 CSS 布局就会豁然开朗。(建议整个 CSS 布局学完后再重新细读深入下。)
盒子的位置摆放
默认情况下,盒子按照元素在 HTML 中的先后位置从左至右自上而下一个接着一个排列摆放。如下图:
在图中我们可以看到,有些元素的盒子被渲染为完整的一行,如h1、p、div;而有些元素的盒子则被渲染为水平排列,直到该行被占满然后换行,如span、a、strong。
这是因为不同的盒子使用的是不同的格式化上下文(formatting context)来布局,每个格式化上下文都拥有一套不同的渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。(就如我们参加结婚喜宴一样,有父母长辈席,好友席,同事席,甚至前男/女朋友席等,不同的身份坐到对应位置即可。)
格式化上下文(formatting context)
我们常见的两个格式化上下文分别为:块格式化上下文(block formatting context 简称 BFC)和行内格式化上下文(inline formatting context 简称 IFC)
BFC
块级盒(block-level boxes)
当元素的 CSS 属性 display 的计算值为 block,list-item,table,flex 或 grid 时,它是块级元素。视觉上呈现为块,竖直排列。典型的如 每个块级元素至少生成一个块级盒,称为主要块级盒(principal block-level box)。一些元素,比如 块级盒参与 BFC,被渲染为完整的一个新行。 默认根元素(html 元素)会创建一个 BFC,其块级盒子元素将会按照如下规则进行渲染: 除此之外,还有其他方法可以创建一个新的 BFC,具体可参看:块格式化上下文。除此之外,flexbox 布局和 grids 布局中的 item 都会创建一个新的 BFC。 具体渲染规则及效果可参看:块格式化上下文 更多关于 BFC 相关内容可参看: 当元素的 CSS 属性 display 的计算值为 inline,inline-block,inline-table,inline-flex 或 inline-grid 时,它是行内级元素。视觉上它将内容与其它行内级元素排列为一行,直到该行被占满然后换行。典型的如段落内容,文本或图片,都是行内级元素。 注:由于目前几乎所有资料都将行内元素当做行内级元素,所以前面的课程我们也遵循这个美丽的错误。严格来说,行内元素不包括 inline-block 的,行内级元素才包括。我们要理解其中的区别,知晓这个美丽的错误。 行内级元素生成行内级盒,参与行内格式化上下文(inline formatting context),被渲染为水平排列, 直到当行被占满然后换行。 行内级盒分为行内盒(inline boxes)和原子行内级盒(atomic inline-level boxes)。前者由非置换元素且 display 值为 inline 的元素生成;后者由行内级置换元素,或 display 值为 inline-block, inline-table, inline-flex, inline-grid 的元素生成。 每一行排列的行内级盒都可以看做由一个匿名的行盒包裹,如下图(使用了两种灰色背景色来模拟): 当块容器盒(block container box)不包括任何块级盒(block-level boxes)时,就会创建一个行内格式化上下文(IFC)。(一般来说一个块级盒也是一个块容器盒,具体可参看: Block-level elements and block boxes) IFC 中的行内级盒将会按照如下规则进行渲染(规则有点多,大概主要点就是行盒,折行机制,水平对齐方式,垂直高度及垂直对齐方式): 注:在 IFC 的环境中,是不能存在块级元素的,如果将块级元素插入到 IFC 中,那么此 IFC 将会被破坏掉变成 BFC,而块级元素前的元素或文本和块级元素后的元素或文本将会各自自动产生一个匿名块盒其包围。 具体行盒高度及垂直对齐方式渲染效果可参看: 更多关于 IFC 相关内容可参看: 除此之外,还有一些其他不同类型的盒子,如下: 而这些盒子也将采用不用的格式化上下文来渲染,如 table formatting context(table 布局)、flex formatting context(flexbox 布局)、grid formatting context(grid 布局)。 更多关于盒子的介绍可参看: 上面我们所讨论的其实都是常规流(normal flow)中盒子的摆放。但实际上我们有三种定位方案,分别为: 默认盒的定位方案就是常规流,但是如果触发了以下任何一个条件,将不会使用常规流: 在常规流中,不同的盒子将采用不同的格式化上下文渲染,也就是上面所讲的部分。 对于浮动定位方案, 盒称为浮动盒(floating boxes)。它位于当前行的开头或末尾。这导致常规流环绕在它的周边,除非设置 clear 属性。 要使用浮动定位方案,元素 CSS 属性 position 必须为 static 或 relative,然后 float 不为 none 。如果 float 设为 left, 则浮动定位到当前位置的开始位置,如果设为 right, 则浮动定位到当前位置的最后位置。 具体介绍可学习下面章节:元素浮动——float。 如果元素的属性 position 不是 static 或 relative, 那它就是绝对定位元素。 对于绝对定位方案,盒从常规流中被移除,不影响常规流的布局。 它的定位相对于它的包含块,定位坐标可通过属性 top、bottom、left、right 来设置 。 固定定位元素(fixed positioned element)也是绝对定位元素,它的包含块是视口。当页面滚动时它固定在屏幕上,因为视口没有移动。 具体介绍可学习下面章节:元素定位——position。 在之前的课程中,我们学习到了 inline-block 的基础知识,接下来我将介绍一些使用 inline-block 产生的问题和解决方法以及其常见的应用场景,来进一步加深了大家对 inline-block 的理解。 我们创建一个导航列表,并将其列表 item 设置为 inline-block,主要代码如下: 效果图如下: 我们从效果图中可以看到列表 item 之间有一点小空隙,但是我们在代码中并没有设置 margin 水平间距。那么这个空隙是如何产生的呢? 这是因为我们编写代码时输入空格、换行都会产生空白符。而浏览器是不会忽略空白符的,且对于多个连续的空白符浏览器会自动将其合并成一个,故产生了所谓的间隙。 对于上面实例,我们在列表 item 元素之间输入了回车换行以方便阅读,而这间隙正是这个回车换行产生的空白符。 同样对于所有的行内元素(inline,inline-block),换行都会产生空白符的间隙。 从上面我们了解到空白符,是浏览器正常的表现行为。但是对于某些场景来说,并不美观,而且间隙大小非可控,所以我们往往需要去掉这个空白间隙。一般来说我们有两种方法来去掉这个换行引起间隙:代码不换行和设置 font-size。 我们了解到,由于换行空格导致产生换行符,因此我们可以将上述例子中的列表 item 写成一行,这样空白符便消失,间隙就不复存在了。其代码如下: 但考虑到代码可读及维护性,我们一般不建议连成一行的写法。 首先要理解空白符归根结底是个字符,因此,我们可以通过设置 font-size 属性来控制产生的间隙的大小。我们知道如果将 font-size 设置为 0,文字字符是没法显示的,那么同样这个空白字也没了,间隙也就没了。 于是顺着这个思路就有了另一个解决方案:通过设置父元素的 font-size 为 0 来去掉这个间隙,然后重置子元素的 font-size,让其恢复子元素文字字符。 所以该方法代码如下: 使用该方法时需要特别注意其子元素一定要重置 font-size,不然很容易掉进坑里(文字显示不出来)。 由于 inline-block 属于行内级元素,所以 vertical-align 属性同样对其适用。 在正式讲解 vertical-align 之前,我们需要先说一些基本概念。 中线(middle)、基线(baseline)、顶线(text-top、底线(text-bottom))是文本的几个基本线,其对应位置如下图: vertical-align 只接受8个关键字、一个百分数值或者一个长度值。下面我们将看看各关键字如何作用于行内元素。 具体 demo 可参考:行内级元素垂直对齐方式 清除浮动主要是为了解决由于浮动元素脱离文流导致的元素重叠或者父元素高度坍塌的问题,而这两个问题分别对应了需要清除浮动的两种种情况:清除前面兄弟元素浮动和闭合子元素浮动(解决父元素高度坍塌)。 清除前面兄弟元素浮动很简单,只需要在不想受到浮动元素影响的元素上使用 在 CSS2 以前,clear 的原理为自动增加元素的上外边距(margin-top)值,使之最后落在浮动元素的下面。在 CSS2.1 中引入了一个清除区域(clearance)——在元素上外边距之上增加的额外间距,使之最后落在浮动元素的下面。所以如果需要设置浮动元素与 clear 元素的间距,得设置浮动的元素的 margin-bottom,而不是 clear 元素的 margin-top。 demo 可见:clear 清除浮动 我们知道,在计算页面排版的时候,如果没有设置父元素的高度,那么该父元素的高度是由他的子元素高度撑开的。但是如果子元素是设置了浮动,脱离了文档流,那么父元素计算高度的时候就会忽略该子元素,甚至当所有子元素都是浮动的时候,就会出现父元素高度为 0 的情况,这就是所谓的父元素高度坍塌问题。为了能让父元素正确包裹子元素的高度,不发生坍塌,我们需要闭合子元素的浮动。 一般我们有两种办法可以用来闭合子元素浮动: 由于我们最后一个元素使用 对于这个方法,以前我们是利用新增一个空元素( 虽然这种办法比较直观,但是不是很优雅,因为增加了一个无用的空白标签,比较冗余而且不方便后期维护(一般不太建议使用该办法)。所以后期有了通过父元素的伪元素(::after)实现的著名 clearfix 方法,代码如下: 上面方法给父元素增加一个专门用于处理闭合子元素浮动的 该方法的原理是:父元素在新建一个 BFC 时,其高度计算时会把浮动子元素的包进来。 下面我们以实例为证:如下图我们的图片为浮动,父元素 article 的高度就出现了坍塌(没有包括图片),而根元素 HTML (默认情况下我们的根元素 HTML 就是一个 BFC)的高度则包括了图片的高度。 既然新建一个 BFC 可以解决父元素高度坍陷问题,那就好办了,下面这些都可以创建一个 BFC : 虽然有这么多方法可用,可我们常用的就是 上面主要讲解了我们比较常的一些清除浮动解决方案,看似简单的清除浮动方法其实则涉及到了很多复杂的CSS规则,大家在实际操作的时候可以针对不同的情况参考上面的方法。 网页正常文档流排版可以理解为在一个平面立面里面,元素占据空间,依次排列,互不覆盖。但是当页面中元素设置了定位属性的时候,难免会出现元素之间相互重叠的情况,比如下图小猫和小狗的图片都设置了绝对定位,2张图片的位置重叠了,小猫图显示在小狗图上面。现在如果我们想要小狗图显示在上面,就需要涉及到 CSS 中的 z-index 属性了。 z-index 属性用于指定已定位元素在垂直于页面方向的排列顺序,其属性值有2种: 一般情况下,z-index 值进行比较有下面2条规则: 所以上面例子,2张图都没设置 z-index 的值,在 dom 结构上排后面的小猫图展示在上面。如果想小狗图展示在小猫图上面,只需要设置小狗图的 z-index 属性值大于0就可以了。 给 上面说到,z-index 默认值 上面例子中,我们给 但是当我们设置了 效果: 其原因就在于我们给 上面例子告诉我们,并不是所有情况 z-index 值大的元素都会在上面,我们在进行 z-index 比较的时候要留意其祖先元素有没有建立独立的层叠上下文,z-index 只有在同一个层叠上下文中比较才有意义。另外,对定位元素设置 z-index 属性不是唯一创建层叠上下文的方法,具有下面属性的元素都会创建层叠上下文(具体可参看:层叠上下文 | MDN): 也就是说,上面例子中, 效果如下: 前面我们学习到了 flexbox 布局。通过使用 flexbox 布局,我们可以更轻松实现以往很难实现的页面布局。本文主要讲解 flexbox 布局是如何去分配和计算布局剩余空间的。(本文阅读前要求你对 flexbox 已经有了初步的认知,如果不是很了解,建议先学习前面视频内容。) 为了更好地理解本文内容,我们需要先了解下面一些基础概念。 flexbox 容器又称弹性容器,通过设置 当设置一个元素为 flexbox 容器时,其直接子元素将自动成为容器成员,也可以称之为:flexbox 项目。 注:因为 flexbox 布局是发生在父元素和子元素之间的,所以下面为了行文方便,统一将 flexbox 容器称为“父容器”,而 flexbox 项目统一称为“子元素” 剩余空间就是指父容器在主轴方向上剩余未分配的空间,它是 flexbox 布局中一个很重要的词。我们可以借助下面的例子来更好地理解: 上面代码,我们定义了一个宽度为600px的父容器 container,以及宽度为200px的子元素 item1 和 item2 ,那么我们得出其剩余空间为200px,计算公式为: flexbox 布局中的子元素可以通过设置 flex-basis 用来定义子元素的默认宽或高。如果父容器 flex-grow 用来指定父容器多余空间的分配比率,默认值为0。看到这里,大家可能还是没有概念。为了更形象地理解,我们一起看下下面的例子。 其 HTML 代码如下: 其 CSS 代码如下: 这里我们对 item1 设置 在这里我们可以得出:其实 flex-grow 即定义如何去分配父容器的剩余空间 ,当值为0时,则子元素都不会索取父容器的剩余空间。当 item1 设置 如果此时我们设置 item2 的 上面的例子,我们只考虑了子元素的宽度总和都是没有超过父容器的宽度的情况,则其可以使用 flex-grow 来分配父容器的剩余空间。那么当子元素的宽度总和超过父容器的宽度时,这时剩余空间还可以分配吗?此时 flex-grow 是否还有效呢?让我们先看看下面的例子: 这里我们设置上面例子的 item1 和 item2 的宽度为350px,则子元素的宽度总和为700px且超过父容器container的600px宽度。 我们可以看到 flex-grow 并没有起到作用,且两个 item 的宽度还被压缩到只有300px。 通过之前学到的如何计算剩余空间的方法,我们可以算出本例子的剩余空间为600px - 700px即 -100px,这里可以得出由于没有剩余空间,则定义了 flex-grow 的子元素能分配到的空间为0,故不生效。另外我们需要知道的是 flexbox 环境的父容器的宽度600px并不会因为子元素的总宽而改变,即子元素的宽度总和最多等于父容器的宽度,所以为了让子元素完整显示在父容器内,只有两个办法: flex-shrink 用来指定父容器空间不够时子元素的缩小比例,默认为1。如果一个 flexbox 项目的 flex-shrink 属性为0,则该元素不会被压缩。 上面我们可以知道,当子元素的宽度总和大于 flexbox 父容器的宽度时,其剩余空间将为负数,如果没有设置换行的情况下,其将会通过压缩子元素来使其能够容纳在父容器内。那么我们如何控制子元素的压缩比例呢?答案就是通过将通过设置 下面例子,我们设置两个 item 的宽度为350px,而容器 container 的宽度仍为600px。同时定义了 item1 和 item2 的 flex-shrink 的属性分别为1和4。如下所示: 代码如下: 我们看到由于缺少100px的空间,按照 item1 和 item2 定义的 flex-shrink 的值,缺少的100px将分成5份。item1 将压缩其中的 1/5 即20px,item2 的将压缩其中的 4/5 即80px。 在上面的知识中,我们了解到 flex-shrink 默认值为1,即默认子元素在父容器空间不足时会被压缩。现在我们把项目的 flex-shrink 设为0来看下不压缩的情况。如下所示: [图片上传中...(image-22b3f3-1542962334288-0)] 代码如下: 我们之前有提到过网格系统,比如960s,bootstrap 的网格系统,但是这些网格系统都是模拟出来的(使用 float 或 flexbox),而并非天生的,虽然可以解决一些常见布局问题,但面临 Win10 UI 还是有点力所不及,如下图: 但是随着 CSS 的不断发展及完善,一种新的网格布局方式被纳入规范,它将会解决所有的网格问题,这就是我们要说的 CSS Grid Layout。 在说 CSS Grid Layout 之前,我们先来看看 excel 的表格。 以我们的 Sheet1 的 A1 单元格为例,先是有上下左右四条线围着,然后定位是由竖直的 A 栏与横向的1行二维坐标表示 A1。如果有需要甚至还还可以和邻近的单元格合并。 现在我们提炼下上面的几个概念:线条,栏(竖直),行(横向),单元格,合并。接下来我们把这些概念对应到我们的网格系统: 网格系统布局其实跟 flexbox 布局差不多,都是由父子元素构成的布局。所以属性分为父元素属性和子元素属性。 因篇幅有限,这里只简单介绍每个属性的用途,具体的属性取值请参考: 这里我们将父元素属性大概分为三大类,其中第一类与第二类属性可以简写为 第一类:如何去定义一个网格系统,行列及间距等。 第二类:自动分配形式,当定义的行或列数量不够时,多出 item 的自动排列方式: 第三类:分布对齐的方式(属性跟 flexbox 的有点像)。 接下来是我们的 item 属性,同样这里我也将它分为两类: 第一类:单元格所占格子多少 第二类:单元格的自定义对齐方式,这个跟 flexbox 的 item 有点相似。 百说不如一练,我们接下来使用网格系统来实战下我们的 Win10 UI,如下图: ) html结构为: 写好结构后,我们就开始使用刚才说得 grid 来实现我们的效果了。先拆分成最小的单元格为 6栏 * 3行,最小单元格的大小为140px,整体内容一屏水平垂直居中。 现在我们可以看到效果如下: 接下来要合并单元格实现我们的最终效果。合并单元格有两种实现方式一种是 line 的开始与结束(包括 colunm 与 row),另一种就是在 grid 上面定义的 area,这里我们使用第一种方法。 这里重提下上面的 Grid Lines 概念,如要实现 n栏 * m行的网格,则需要n+1条垂直line,m+1条水平线。 第一个 item 元素单元格占了两列,第一列和第二列,那么就垂直栏开始于第一条 line,结束于第三条 line,同样第5个 item 元素也是如此 而第二个 item 元素栏和行都跨了两个,CSS 代码如下: 同样第七个 item 元素行跨了两个,第9个 item 元素栏跨了两个,CSS 代码如下: 这个布局就这么简单的完成了,效果可见 demo 现代浏览器最新版本基本上都支持了 CSS Grid Layout,下图是 caniuse 上的支持情况: 有些浏览器旧版本的已经实现但是没有默认开启(chrome < 57,firefox < 52)则需要通过下面的方式手动设置开启: 上面只是 grid 布局的简单使用,实际上 grid 布局十分强大,使用起来也十分方便,未来将会是布局的主力军,但是目前由于兼容问题暂时不建议在生产环境中使用,不过我们相信很快就可以见识到 grid 布局的强大威力了。 我们现在已经知道,使用 HTML & CSS 可以搭建出一个漂亮的 Web 页面。 那么浏览器到底是如何使用我们的 HTML & CSS 渲染成我们在屏幕上所看到的页面呢? 虽然具体渲染过程很复杂,但是还是可以将其分为几个关键路径,如下: 首先浏览器渲染页面前会根据 HTML 结构构建成对应的 DOM 树。 以下面的 HTML 代码为例: Hello web performance students! 其 DOM 树生成的流程如下图: [图片上传中...(image-b05031-1542962693660-5)] 整个流程的最终输出就是我们这个简单页面的文档对象模型 (DOM),如下图: [图片上传中...(image-2b953-1542962693660-4)] 在浏览器构建上面的 DOM 时,在文档的 head 部分遇到了一个 link 标记,该标记引用一个外部 CSS 样式表: 与处理 HTML 时一样,我们需要将收到的 CSS 规则转换成某种浏览器能够理解和处理的东西。因此,我们会重复 HTML 过程,不过是为 CSS 而不是 HTML: CSS 字节转换成字符,接着转换成令牌和节点,最后挂靠到一个称为“CSS 对象模型”(CSSOM) 的树结构内: [图片上传中...(image-d167f0-1542962693660-2)] CSSOM 为何具有树结构?这是因为浏览器为页面上的任何对象计算最后一组样式时,都会先从适用于该节点的最通用规则开始(例如,如果该节点是 body 元素的子项,则应用所有 body 样式),然后通过应用更具体的规则(即规则“向下级联”)以递归方式优化计算的样式。 以上面的 CSSOM 树为例进行更具体的阐述。span 标记内包含的任何置于 body 元素内的文本都将具有 16 像素字号,并且颜色为红色 — font-size 指令从 body 向下级联至 span。不过,如果某个 span 标记是某个段落 (p) 标记的子项,则其内容将不会显示。 接下来就是将 DOM 树与 CSSOM 树合并形成渲染树。 渲染树会网罗网页上所有可见的 DOM 内容,以及每个节点的所有 CSSOM 样式信息。 [图片上传中...(image-293e8f-1542962693660-1)] 注:渲染树只包含渲染网页所需的节点,如 有了渲染树,我们就可以进入“布局”阶段。 到目前为止,我们计算了哪些节点应该是可见的以及它们的计算样式,但我们尚未计算它们在设备视口内的确切位置和大小---这就是“布局”阶段,也称为“自动重排”。 为弄清每个对象在网页上的确切大小和位置,浏览器从渲染树的根节点开始进行遍历。让我们考虑下面这样一个简单的实例: 以上网页的正文包含两个嵌套 div:第一个(父)div 将节点的显示尺寸设置为视口宽度的 50%;而里面内嵌的第二个 div 将其宽度设置为其父项的 50%,即视口宽度的 25%。如下图: 布局流程的输出是一个“盒模型”,它会精确地捕获每个元素在视口内的确切位置和尺寸:所有相对测量值都转换为屏幕上的绝对像素。 最后,既然我们知道了哪些节点可见、它们的计算样式以及几何信息,我们终于可以将这些信息传递给最后一个阶段:将渲染树中的每个节点转换成屏幕上的实际像素形成我们可见的页面。这一步通常称为“绘制”或“栅格化”。 注:执行渲染树构建、布局和绘制所需的时间将取决于文档大小、应用的样式,以及运行文档的设备:文档越大,浏览器需要完成的工作就越多;样式越复杂,绘制需要的时间就越长(例如,单色的绘制开销“较小”,而阴影的计算和渲染开销则要“大得多”)。 一个页面渲染完毕后,随着用户的操作,或者数据变化,网页还会进行重新渲染。根据不同的触发条件,重新渲染分为两种情况:重排(reflow)和重绘(repaint)。 所有对元素视觉表现属性的修改,都会导致重绘(repaint)。比如修改了背景颜色、文字颜色等。 所有会触发元素布局发生变化的修改,都会导致重排(reflow)。比如窗口尺寸发生变化,删除、添加 DOM 元素,修改了影响元素盒子大小的 CSS 属性如 通常情况下,重排的影响更大,重排会导致文档局部或全部的重新运算:重新计算元素位置,大小。(改变一个元素的布局,可能会影响很多个元素的布局) 不管是重绘还是重排导致的重新渲染,都会阻塞浏览器。重新渲染的的过程中,JavaScript 会被阻塞,用户的交互行为也会被卡住。复杂的 CSS 动画甚至会拖慢 JavaScript 的运行速度。 注:本文涉及到的 JavaScript 部分,可以先忽略,等以后学习了 JavaScript 再来查看。 网站 CSS trigglers 列出了所有 CSS 属性对 layout (布局)、paint (绘制)的影响。通过这个表,可以查到不同内核下,对 CSS 属性的修改会导致重绘、重排还是两者都会发生。 注:Composite (渲染层合并) 是 chrome 引入 GPU 加速带来的新概念。(相关信息可参看下面的参看资料) 对 CSS 属性进行修改,包括但不限于以下场景: 所有的最佳实践都是围绕尽最大可能的降低重绘和重排的频率,来达到减少重新渲染的目的。 浏览对 CSS 属性的连续修改做了优化,比如下面的连续修改两次 style,不会导致两次重新渲染: 上面代码只会进行一次重新渲染。但是如果写的不好,则会触发两次重新渲染,如下: 上面代码对 div 元素设置背景色以后,第二行要求浏览器给出该元素的位置,所以浏览器不得不重新渲染然后得到该元素的位置。 除此之外,样式的写操作之后,如果有下面这些属性的读操作,都会引发浏览器立即重新渲染: 上面对通过对 style 对 CSS 属性一个一个修改,其实更好的方式应该是通过 class 来批量化。 元素等都是块级元素。
,生成额外的盒来放置项目符号,不过多数元素只生成一个主要块级盒。
渲染规则
IFC
行内级盒(inline-level boxes)
渲染规则
其他格式化上下文
定位方案
常规流(normal flow)
浮动(float)
绝对定位(absolute position)
参考资料
四、深入了解 inline-block
水平间隙问题
.nav {
background: #999;
}
.nav-item{
display:inline-block; /* 设置为inline-block */
width: 100px;
background: #ddd;
}
如何消除空白符
代码不换行
设置 font-size
.nav {
background: #999;
font-size: 0; /* 空白字符大小为0 */
}
.nav-item{
display:inline-block;
width: 100px;
font-size: 16px; /* 重置 font-size 为16px*/
background: #ddd;
}
对齐问题
中线、基线、顶线、底线
x
的下端沿。x
的中间。vertical-align 的值
值
描述
baseline
默认元素的基线与父元素的基线对齐。
sub
将元素的基线与其父元素的下标基线对齐。
super
将元素的基线与其父代的上标 - 基线对齐。
text-top
将元素的顶部与父元素的字体顶部对齐。
text-bottom
将元素的底部与父元素的字体的底部对齐。
middle
将元素的中间与基线对齐加上父元素的x-height的一半。
top
将元素的顶部和其后代与整行的顶部对齐。
bottom
将元素的底部和其后代与整行的底部对齐。
将元素的基线对准给定长度高于其父元素的基线。
像<长度>值,百分比是line-height属性的百分比。
五、清除浮动详解
清除前面兄弟元素浮动
clear:both
即可, HTML & CSS 代码如下:.fl {
float: left;
}
.fr {
float: right;
}
.cb {
clear: both;
}
闭合子元素浮动
clear: both
clear:both
clear:both
,所以该元素就能不受浮动元素影响出现在父元素的最底部,而父元素计算高度的时候需要考虑到这个正常元素的位置,所以高度自然包裹到了最底部,也就没有了坍塌。 或
或
.box {
float: left;
}
.clear-box {
clear: both;
}
.clearfix::after {
content:"";
display:table;
clear: both;
}
clearfix
类名,该类使用 ::after
伪元素类选择器增加一个内容为空的结构来清除浮动,可能你们比较疑惑的是为什么要设置 display:table
属性,这其实涉及到一个比较复杂的进化过程,具体可以参考资料——clearfix浮动进化史新建 BFC
overflow: hidden
,代码如下:.container {
overflow: hidden;
}
.box {
float: left;
}
总结
六、深入了解 z-index
z-index
auto
(默认值)和整数。这里有2个需要注意的点:
auto
, z-index 可以设置为任意整数,正数,0,负数都可以。
auto
数值上相当于0)。
.dog {
position: absolute;
top: 10px;
left: 100px;
z-index: 1; /* 设置小狗图的 z-index 值大于0 */
}
.cat {
position: absolute;
top: 80px;
left: 70px;
}
.dog
元素增加了 z-index: 1
属性就可以让小狗图相比于小猫图在垂直于页面的方向上离我们更近,这样效果就是小狗图显示在上面了。层叠上下文
auto
数值上等于0,那设置了 z-index:0
和 默认的 z-index:auto;
有没有区别呢? 答案是有区别的。区别在于设置了 z-index 属性为整数值(包括0)的元素,自身会创建一个层叠上下文。而创建一个层叠上下文之后,其子元素的层叠顺序就相对于父元素计算,不会与外部元素比较。这样说比较抽象,我们来看个例子。img {
width: 200px;
}
.dog-container {
width: 200px;
height: 100px;
background: red;
position: relative;
z-index: auto; /* 默认值auto */
}
.dog {
position: absolute;
top: 10px;
left: 100px;
z-index: 2;
}
.cat {
position: absolute;
top: 80px;
left: 70px;
z-index: 1;
}
.dog
和 .cat
增加了容器 .dog-container
和 .cat-container
, 并且 .dog
和 .cat
都设置了 z-index
值,所以都显示在红色背景的 .container
之上,而且 .dog
z-index 数值比较大,所以显示在上面。.dog-container
的 z-index 属性值为0之后,我们发现,z-index 值比较大的 .dog
元素反而到 z-index值比较小的 .cat
下面了.dog-container {
width: 200px;
height: 100px;
background: red;
position: relative;
z-index: 0; /* 将 z-index 值改成0 */
}
.dog-container
设置了 z-index:0
之后,.dog-container
就创建了自己的层叠上下文,其子元素 .dog
在比较层叠顺序的时候只会在 .dog-container
内比较,而不会与外面的 .cat
比较。如下图所示:
.dog-container
满足上面任意一条属性,也会一样出现上面的情况。比如设置 opacity 属性:.dog-container {
width: 200px;
height: 100px;
background: red;
position: relative;
opacity: 0.6; /* 设置 opacity 属性小于1也会创建层叠上下文 */
}
总结
参考文档
七、flexbox 剩余空间分配规则
基础概念
flexbox 容器 (flexbox container)
display: flex
而产生,简单示例如下:.container {
display: flex; /* 或者 inline-flex */
}
flexbox 项目 (flexbox item)
剩余空间
剩余空间 = 父容器的宽度 - item1的宽度 - item2的宽度
剩余空间分配相关属性
flex
属性来改变其所分配到的空间大小。flex
属性包括了 flex-basis
、 flex-grow
、flex-shrink
flex-basis
flex-direction
属性的方向为水平方向则为宽度,如为垂直方向则为高度。相当于给子元素设置宽或高。如果同时设置了该属性与宽或高,则该属性权重大于宽或高的值。flex-grow
例子: 只设置 item1 的 flex-grow 为1
/* css 部分 */
.container {
display: flex;
width: 600px;
height: 140px;
align-items: center;
background-color: #ddd;
}
.item {
width: 200px;
height: 120px;
line-height: 120px;
text-align: center;
background-color: orange;
}
.item2 {
background-color: green;
}
.item-flex-grow{
flex-grow:1;
background-color:
}
flex-grow:1
后,我们可以看到 item1 的所占空间宽度变成400px,也就是说 item1 把之前我们所说的父容器剩余的200px空间都占用了。flex-grow: 1
的时候,由于 item2 没有设置 flex-grow 的值,则剩余空间将会被分成一份,并且分别分给了 item1。例子: 设置 item1 的 flex-grow 为1,且 item2 的 flex-grow 为3
flex-grow:3
,item2 也将会参与索取父容器的剩余空间,此时父容器的剩余空间将分为4份,然后1份分配到 item1,而分配3份到 item2,如下图:如果子元素的宽度的总和超过父容器,flex-grow 将不生效。
flex-shrink
为什么需要 flex-shrink 来定义缩小比例呢?
flex-shrink
这个属性。例子:设置项目的flex-shrink
/* 这里只展示关键css */
.container {
display: flex;
width: 600px;
height: 140px;
}
.item {
width: 350px;
height: 120px;
}
.item1 {
flex-shrink: 1;
}
.item2{
flex-shrink: 4;
}
例子:设置项目的 flex-shrink 为0
/* 这里只展示关键css */
.container {
display: flex;
width: 600px;
height: 140px;
}
.item {
width: 350px;
height: 120px;
flex-shrink: 0;
}
更多阅读
八、grids 布局系统
网格系统基础概念
网格系统基本属性
父元素(Grid Container)属性
grid
属性(不包括 display
属性):
space-evenly
值,补足了以前flex的 space-around
和 space-between
的不足子元素(Grid Item)属性
实例演示
html,body {
height: 100%;
}
.grid {
height: 100%;
display: grid; /* 网格布局 */
/* 整体水平垂直居中 */
justify-content: center;
align-content: center;
/* 定义6栏3行 */
grid-template-columns: 140px 140px 140px 140px 140px 140px;
grid-template-rows: 140px 140px 140px;
/* 定义item之间的间距为20px */
grid-gap: 20px;
background: #efefef;
}
.item{
background: #ccc;
}
.item:nth-child(1),
.item:nth-child(5) {
grid-column: 1 / 3; /* 起始于1,结束于3 */
}
.item:nth-child(2) {
grid-column: 3 / 5; /* column起始于3,结束于5 */
grid-row: 1 / 3; /* row起始于1,结束于3 */
}
.item:nth-child(7) {
grid-column: 6;
grid-row: 2 / 4; /* row起始于2,结束于4 */
}
.item:nth-child(9) {
grid-column: 2 / 4; /* column起始于2,结束于4 */
}
浏览器支持
总结
参考资料
九、浏览器如何渲染 HTML & CSS
构建 DOM 树
CSSOM
style.css
。由于预见到需要利用该资源来渲染页面,它会立即发出对该资源的请求,并返回以下内容:/* style.css */
body { font-size: 16px }
p { font-weight: bold }
span { color: red }
p span { display: none }
img { float: right }
合并渲染树
display: none;
的元素是不会出现在渲染树种的。布局及绘制
参考资料
十、重排与重绘
width
、 height
、 padding
等。导致重排和重绘的场景
CSS 属性改变
display: none
隐藏 DOM 节点(导致重绘和重排)visibility: hidden
隐藏 DOM 节点 (导致重绘,因为它没有影响其它元素位置布局)用户交互
:hover
、进入文本输入框、修改浏览器的字体都会导致重排最佳实践
CSS 属性的读、写操作分开
div.style.color = 'blue';
div.style.marginTop = '30px';
div.style.color = 'blue';
var margin = parseInt(div.style.marginTop);
div.style.marginTop = (margin + 10) + 'px';
offsetTop/offsetLeft/offsetWidth/offsetHeight
scrollTop/scrollLeft/scrollWidth/scrollHeight
clientTop/clientLeft/clientWidth/clientHeight
getComputedStyle()
通过 class 或者 csstext 来批量更新样式
// bad
var left = 10;
var top = 10;
el.style.left = left + "px";
el.style.top = top + "px";
// good
el.className += " theclassname";
// good
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
其他方法
display: none
进行样式批量更新:先将元素设为 display: none
(需要1次重排和重绘),然后对这个节点进行100次操作,最后再恢复显示(需要1次重排和重绘)。这样一来,你就用两次重新渲染,取代了可能高达100次的重新渲染。position
属性为 absolute
或 fixed
的元素,重排的开销会比较小,因为不用考虑它对其他元素的影响。visibility : hidden
的元素只对重绘有影响,不影响重排。table-layout: fixed
属性,这个属性的目的是让后面单元格的宽度由表头的宽度来决定——减少布局的计算量。参看资料