如果你在日常工作中使用 CSS,你的主要目标可能会重点围绕着使事情“看起来正确”。如何实现这一点经常是远不如最终结果那么重要。这意味着比起正确的语法和视觉结果来说,我们更少关心 CSS 的工作原理。

CSS 的视觉结果通常是操作隐藏属性的间接后果,你可能还没有意识到这一点。某些 CSS 属性(比如 background-color)与你看到的内容有直接而明显的关系。同时,其它像 display 这样的属性对于我们很多人来说仍然是模棱两可的,因为结果似乎与上下文环境有很大关系。所以...我们需要对于CSS进行再次的整理和探索

渲染过程概述

当加载一个 HTML 文档时,为了让该页面渲染,有很多事情要发生。

第一个步骤就是解析 HTML 文档。浏览器从这一步创建一个文档树。树状结构是表示像 HTML 这种具有明显层次结构信息的一种方法。树中的元素可以被描述为类似于族谱,比如祖先、父亲、孩子和兄弟姐妹。

你可能听说过术语 DOM。它代表文档对象模型(Document Object Model)。这是文档树结构的一种扩展,被用于存储和操作 Web 文档内容有关的信息。

随着 HTML 被解析,样式表和其它资源也被获取。样式声明通过一个称为层叠的过程来解释和确定。

在此过程期间,CSS 属性的最终值被确定。在计算后,这些值可能与样式表中定义的有所不同。例如,像 auto 这种关键字和相对单位被指派了真实值,并且继承值被应用了。这些计算值被存储在一个树中,类似于 DOM 中的元素,毫不奇怪,它被称为 CSS 对象模型或者 CSSOM。

现在才有可能开始渲染页面的过程。这个过程的第一步是盒模型的计算。这是一个算出元素的尺寸和间距的重要步骤,虽然不一定是元素的最终位置。

与盒模型相比,不那么被人熟知的是一个称为视觉格式化模型的过程。这个过程确定元素在页面上的布局和位置。它包含了一些你可能已经熟悉的概念,比如定位方案、格式化上下文、显示模式和堆叠上下文。

最后,页面被渲染。

上面的段落中可能有一些你还不熟悉的一些术语。如果是这样的话,最重要的是要理解层叠、盒模型以及视觉格式化模型。这些术语都是解释、处理和渲染 HTML 和 CSS 的核心步骤。我们下面要更详细地研究一下这三个步骤。

层叠

层叠可能是最被误解的 CSS 特性之一。它是指组合不同样式表以及解决 CSS 选择器之间冲突的过程。

层叠查看声明的重要性、来源、特殊性以及顺序,来确定使用哪个样式规则。

你需要知道什么:

大多数网站有多个样式表。通常,样式是用引用一个 CSS 文件的一个 link 标记,或者 HTML 主体部分的 style 标记添加的。即使最基础的页面也会有浏览器提供的默认样式。这种默认样式表有时被称为 user-agent 样式表。

在层叠期间,样式表是按如下顺序被解释的:

  1. !important 声明

  2. 作者的样式表

  3. 浏览器默认的样式表

注意:这里我跳过了用户样式表,是因为它们不再常见,可能不会是读这本文的所有人要考虑的因素。

在组合这些来源后,如果多个规则应用到同一元素,就用特殊性来确定应用哪条规则。

特殊性

特殊性(Specificity)是给选择器的权重。它常被误认为是一个数字。实际上是 4 个单独的数字或者 4 种权重类别。

要计算特殊性,要统计如下选择器的数目:

  1. ID 选择器

  2. 类选择器、属性选择器和伪类选择器

  3. 元素选择器和伪元素选择器

例如:#nav .selected:hover > a::before 将是 1, 2, 2

无论有多少个类选择器,都不会比一个 ID 选择器有更高的特殊性。当比较选择器时,我们首先比较 ID 选择器的特殊性。只有 ID 选择器相等时,才比较类选择器、属性选择器和伪类选择器的特殊性值。如果最后依然相等,才比较元素和伪元素选择器。

如果每个类别的特殊性都是相等的,那么来源中最后的声明获胜。

是的!我知道我说的是 4 个类。行内样式比 ID 选择器有更高的特殊性。不过由于在技术上行内样式在特殊性计算中是第一类,通常你不会与行内样式竞争,所以很容易记住行内样式的特殊性总是会占优。

重要的注意事项:虽然 !important 声明不会作为特殊性计算的因素,不过它们比层叠中的普通声明有更大的优先级。

继承

继承不是层叠的一部分,不过我在这里把它包含进来,是因为它经常被与层叠结合在一起讨论。

继承是应用给一个元素的值可以被传递到(或者继承)子元素上的过程。

你肯定知道这个事实,即当把字体属性应用到 body 或者其它容器元素时,该属性也会被容器内的所有元素所继承。这就是继承。

并非所有属性默认都被继承。理解继承是编写更优雅更简洁 CSS 的关键。有时候用 inherit 关键字强制继承会相当有用。

注意: 有些属性(比如 border-color)默认值是 currentcolor。也就是说,它们会使用在 color 属性上设置的值。默认值与继承不是一码事。不过,color 属性本身经常是被继承的,所以我倾向于认为这是一种事实上的继承。

盒模型

理解盒模型是布局和定位时防止挫败所必需的。盒模型是 CSS 中最基本的概念之一。

盒模型用于计算元素的宽度和高度。它只是计算过程中的一个步骤,确定元素的最终布局和定位并非完全依赖于它。

你需要知道什么:

HTML 中的每个元素都是一个矩形的盒子。每个盒子有用元素的外边距(margin)、边框(border)、内边距(padding)和内容区定义的四个区域。

默认情况下,当我们设置一个元素的宽度时,只是设置内容区的宽度。当给一个元素添加内边距、边框或者外边距时,是增加了除宽度以外的部分。实际上,这就是说宽度为 50% 的两个元素如果添加了内边距、外边距或者边框,就不会并排填满宽度(因为已经超过了 100% 宽度)。

当设置元素的背景时,不仅会填充内容区,还会填充内边距区和边框区。

概念上,我们把一个 HTML 元素当作是一个东西,所以很容易认为一个元素的视觉边界等于它的宽度,不过实际却并非如此。虽然一个元素的视觉边界包含了内边距和边框区,不过 width 属性是显式地被应用到内容盒。

Width Auto

另一个潜在的困惑来源是 width: auto 的工作机制。宽度为 auto 是大多数 HTML 元素的默认元素,对于像 div、p 这种块元素来说,auto 会计算宽度,这样外边距、边框、内边距以及内容区都组合在一起也能刚好放在可用空间内。

在这种情况下,感觉添加内边距和外边距会向内挤压内容,不过实际上,宽度会被重新计算,以确保所有东西都能刚好放下。相比之下,在设置宽度为 100% 时,光内容区就会把可用空间填满,而不管外边距、内边距和边框。

Box-sizing

box-sizing 属性会改变盒模型的工作方式。当 box-sizing 被设置为 border-box 时,内边距和边框会减少内容区的内部宽度,而不是添加元素的整体宽度。也就是说,元素的宽度现在与其视觉宽度是相同的。

视觉格式化模型

盒模型计算元素的大小,而视觉格式化模型(Visual Formatting Model)负责确定这些盒子的布局。视觉可视化模型考虑盒子的类型、定位方案、元素之间的关系以及内容施加的约束,来确定页面上每个元素的最终位置和呈现。

你需要知道什么:

视觉格式化模型遍历文档树,根据 CSS 盒模型生成一到多个渲染元素所需的盒子。CSS 的 display 属性在确定一个元素如何参与当前上下文和定位方案中发挥关键作用。这些部分综合在一起,确定元素的最终布局和定位。

这是一个复杂的步骤,也是目前为止最难尝试和总结的。如果你还没了解所有这些知识的话,没有关系。理解如何通过 CSS 属性操纵定位方案和格式化上下文是个好的开端。如果你理解了该模型的不同部分之间的相互作用的话,你就会比大多数人做得更好。起码你应该知道它们的存在。

Display 类型

我们知道,在 CSS 中设置 display 属性可以确定如何渲染元素,但是还不清楚其工作原理。事实上,它有时甚至好像是不可预测的。

这是因为,display 属性确定了元素的”盒子类型“。这个隐藏属性由一个内部显示类型和一个外部显示类型组成,二者在一起帮助确定如何渲染元素。

外部显示类型通常要么是解析为 block,要么是解析为 inline,并且与我们对 CSS 中这些 display 属性的期望几乎是一致的。从技术上讲,外部显示类型规定一个元素如何参与其父元素的格式化上下文。

内部显示类型决定元素会生成什么样的格式化上下文。这会影响其子元素的布局排列方式。

想想弹性盒容器的工作机制。其外部类型是 block,内部类型是 flex。它的子元素也可以有一个 block 外部类型,不过子元素的布局是受弹性盒容器的格式化上下文所影响的。

思考这种问题的一个方法是,display 的职责是在元素及其父元素之间共享的。

格式化上下文

格式化上下文都是与布局有关。它们是控制容器内元素的布局以及它们之间如何相互作用的规则。

有些格式化上下文可以直接在容器上设置,比如通过用 display 属性值 flexgridtable。其它类型的格式化上下文,比如块格式化上下文和行内格式化上下文可以被浏览器根据需要创建。

注意:在过去,因为块格式化上下文与浮动交互的方式,理解如果让浏览器建立一个新的块格式化上下文是很重要的。设置为块格式化上下文的元素会包含浮动。不过现在它没有以前那么重要。事实上,它甚至都不是现代清除浮动技术的工作原理了。

定位方案

一个盒子可以根据三种定位方案之一来布局,包括常规流、浮动和绝对定位。你可能熟悉浮动和绝对定位,因为在编写 CSS 时,我们会更直接与这两个打交道。常规流只是一个在元素没有浮动或者定位时默认定位方案的名字。

常规流

常规流(Normal Flow)描述了默认定位方案,'in-flow'(流内)描述遵从该方案的元素。你可以把流内当作是元素根据其源顺序和格式化上下文布局时的自然位置。

浮动

浮动(float)是一个 CSS 属性,它会导致元素脱离常规流,尽可能远地向左或者向右移动,直到它挨着其包含盒或者另一个浮动元素的边缘。当浮动发生时,文本和行内元素会环绕着浮动元素。

正常情况下,如果没有设置浮动,一个元素的高度会调整以容纳其所有的后代元素。当元素被浮动时,就会从流中脱离出来,这意味着容器不会调整其高度以清除它们。

正是这种行为,让多行文本、标题以及其它元素环绕着浮动内容而流动。但是有时这是有问题的。清除浮动并设立新的块格式化上下文会导致容器清除其浮动子元素。这种技术允许浮动被用在布局上,已经成为 Web 开发技术的奠基石很久了。它依然是要知道的重要技术,不过正在逐渐被像 Flexbox 和 Grid 这种更新的布局技术所替代。

绝对定位

绝对定位的元素是彻底从流中删除,并且不像浮动元素,它们对周围的内容没有影响。

相对定位的容器允许我们用绝对定位来控制后代元素的偏移。

相对定位元素也可以设置偏移,不过这种偏移是相对于父元素的常规位置,而不是另一个相对定位的容器。

CSS 属性 topbottomleftright 被用于计算盒子的偏移。这些属性都不是二维偏移,不过都可以相对于其容器的内容盒来定位每个边。

具有重叠偏移的定位元素可以导致元素占用同一空间。堆叠上下文用于解决这个问题。

堆叠上下文

堆叠上下文决定渲染到页面上的东西的顺序。你可以把一个堆叠上下文当作一个层。堆叠底部的层先绘制,该堆叠之上更高的元素出现在上面。

在一个绝对或者相对定位元素上设置 z-index 属性是建立新的堆叠上下文的最常见手段。不过,有很多组成堆叠上下文的其它手段,包括设置透明度(opacity)、转换(transform)、滤镜(filter)或者使用 will-change 属性。

不过,使用这些其它手段的原因并不直观,并且比开发者的预期相比,这些手段对渲染性能有一定影响。只不过它们有助于理解这些层可以被浏览器单独渲染。因此,有时因为性能的原因,有意创建新的堆叠上下文是很有用的。

除非建立了堆叠上下文,否则设置 z-index 是没有效果的。z-index 越高,层在堆叠中放在的位置越高。

有关堆叠的最让人感到困惑的部分之一是,一个新的堆叠上下文可以建立在一个已有的堆叠上下文内。也就是说可以有层中层。

在这种情况下,并非总是最高的 z-index 会放在最上面。

写在最后

  太久没有过好好写博客了...感觉好多东西仿佛你理解和写出来完全是两个不同的概念...庆幸自己憋着气还是把想写的内容写出来了。其实关于CSS,我一直都不认为是一个简单的样式表而已。深入去研究,会发现其难度并不比JS的各种理论知识轻松。后面我会尝试去更深入的去分析CSS中的各种属性,以及认真研究研究grid和flex布局的优势。

  完结撒花~

  PS:参考资料:《CSS权威指南(第3版)》

大前端学习笔记【七】关于CSS再次整理的更多相关文章

  1. 大前端学习笔记整理【七】HTTP协议以及http与https的区别

    前言 还是老样子,新博客开始前总是想先啰嗦几句...HTTP协议其实在当初学习java时老师就有提过...但是...反正就那么过去了... 这段时间公司的项目正好要求做https的转换和迁移,然后自己 ...

  2. 大前端学习笔记整理【一】CSS盒模型与基于盒模型的6种元素居中方案

    概览 CSS盒模型,规定了元素框来处理元素的 内容.内边距.边框和外边距的方式 元素部分是指内容部分,也是最实际的内容,包围内容的称之为内边距,内边距外围是边框,边框外围就是外边距:且外边距是透明的, ...

  3. 大前端学习笔记整理【二】CSS视觉格式化模型

    1. 概念 在视觉格式化模型中,文档树中的每个元素都将会根据盒模型产生零到多个盒子.这些盒子的布局由如下因素决定: 盒子的尺寸和类型 定位策略(正常文档流,浮动或者绝对定位) 和文档树中其他元素的关系 ...

  4. 大前端学习笔记整理【四】LESS基础

    第一次接触CSS预编译,然后对比后发现其实less的上手容易度确实比sass高不少,再加上公司项目也是使用的less.所以想想还是根据网上的各种教程,整理出来了一些比较基础的.而且比较能让我们这种初学 ...

  5. 大前端学习笔记整理【五】rem与px换算的计算方式

    前言 这段时间的小项目中算是真正意义上使用了rem来进行移动端的页面布局,项目结束了我反思了一下之前的对于rem的使用...原来我以前对rem用法完全是在搞笑啊!!结合这次这个小项目,我觉得我也有必要 ...

  6. 大前端学习笔记整理【五】关于JavaScript中的关键字——this

    写在前面 工作有那么一段时间了,但是在工作中,发现自己的理论知识还是有所欠缺.特别是在javascript上,很多东西其实自己属于知道要用这个,但是不知道为什么要这么用...这种情况很是尴尬了,所以写 ...

  7. 前端学习笔记之HTML/CSS 速写神器 Emmet

    HTML/CSS 速写神器:Emmet 在前端开发的过程中,一个最繁琐的工作就是写 HTML.CSS 代码.数量繁多的标签.属性.尖括号.标签闭合等,让前端们甚是苦恼.于是,我向大家推荐 Emmet, ...

  8. 【前端学习笔记】关于CSS通过一个块改变另一个块的样式

    <body><div id="a" style="background:#0F0; height:100px; width:100px;"&g ...

  9. 大前端学习笔记整理【六】this关键字详解

    前言 在上一篇博客里我总结了下辨认this指向的四种方式,但是有师兄抛出一个问题来,我发现那些this的指向并不能说明更复杂的情况,先看下这段代码 var a = { name: 'a', getNa ...

随机推荐

  1. Python-Unittest

    TestCase——> Test Fixure测试固件 | TestSuite测试套件——>TestRunner测试运行器 | TestReport 测试断言 verbosity=2 0代 ...

  2. 超简单(两步)-微信怎么实现打开外部浏览器,下载app,打开网页URL

    现在微信渠道可以说是拉新最快的渠道,因为微信具备强裂变性.但是目前微信对第三方下载链接的拦截是越来越严格了,那么想要在微信内肆无忌惮地推广链接就需要用到微信跳转浏览器的接口,那如何获取该接口呢?   ...

  3. RFCN配置参数

    最近一直被人问这个,索性画张图,省得一遍一遍解释.

  4. CSS 背景图像 背景图片定位

    背景图片定位 background-position属性可以给背景图片定位. background-position属性有两个值,第一个值是水平位置,第二个值是垂直位置.这两个值可以使用百分比来表示( ...

  5. 如何自定义Django数据库中的字段

    新建完项目之后,想要自定义字段 # 创建一个自定义的字段 class MycharField(models.Field): def __inif__(self, max_lenth, *args, * ...

  6. @Transactional 事务说明

    这里面有几点需要大家留意:A. 一个功能是否要事务,必须纳入设计.编码考虑.不能仅仅完成了基本功能就ok.B. 如果加了事务,必须做好开发环境测试(测试环境也尽量触发异常.测试回滚),确保事务生效.C ...

  7. iview table中使用Poptip

    h('Poptip', { props: { confirm: true, // 'ok-text':"yes", // 'cancel-text':"no", ...

  8. 基于django的博客系统

    这是前段代码 达到的效果并不是太好,但我还是要发出来,有更好的建议可以和我讨论 后台还算可以 添加了分类和文章两个功能,还在优化,敬请期待....

  9. failed to find global analyzer [uax_url_email]

    ES的默认分词设置是standard,这个在中文分词时就比较尴尬了,会单字拆分,比如我搜索关键词“清华大学”,这时候会按“清”,“华”,“大”,“学”去分词,然后搜出来的都是些“清清的河水”,“中华儿 ...

  10. 【1天】黑马程序员27天视频学习笔记【Day02】

    02.01常量的概述和使用 * A:什么是常量    * 在程序执行的过程中其值不可以发生改变 * B:Java中常量的分类    * 字面值常量    * 自定义常量(面向对象部分讲) * C:字面 ...