如果你在日常工作中使用 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. Java开发体系

    蓦然回首自己做开发已经十年了,这十年中我获得了很多,技术能力.培训.出国.大公司的经历,还有很多很好的朋友.但再仔细一想,这十年中我至少浪费了五年时间,这五年可以足够让自己成长为一个优秀的程序员,可惜 ...

  2. 如果在springboot中设置了 修改了默认页面的指向 使用thymleaf模板 而请求不到静态资源 可能是

    @Configurationpublic class DefaultView extends WebMvcConfigurerationSupport { @Override public void ...

  3. 设计在canal中的运用,看到随手记下

    观察者模式,定义添加修改删除对应的操作 系统很多Monitor/Listener都是类似 Monitor内含listener,调用再触发 public synchronized void start( ...

  4. java过滤emoji表情

    import java.util.regex.Matcher; import java.util.regex.Pattern; public class test { /** * 表情过滤 * */ ...

  5. F查询和Q查询,事务及其他

    F查询和Q查询 F查询 在上面所有的例子中,我们构造的过滤器都只是将字段值与某个我们自己设定的常量做比较.如果我们要对两个字段的值做比较,那该怎么做呢? Django 提供 F() 来做这样的比较.F ...

  6. python中网络编程之线程

    网络编程之线程 什么是线程? 程序的执行线路.每个进程默认有一条线程.线程包含了程序的具体步骤. 多线程就是一个进程中有除主线程(默认线程)外还有多个线程. 线程与进程的关系(进程包含线程,而线程依赖 ...

  7. JSP 有些类can not be resolved

    看了网上的帖子,切换了jdk到低版本,发现还是不能解决问题. 发现出现问题的代码在tomcat下的Lib包中的其中一个包,jasper.jar 我在想是不是tomcat的版本问题,拷贝了其他地方的ja ...

  8. Oracle 批量修改字段长度

    Oracle 批量修改字段长度 SELECT  'alter table '||a.table_name||' MODIFY  '||A.COLUMN_NAME||' VARCHAR2(100);' ...

  9. vue-cli使用vux时报错处理,“You may need an appropriate loader to handle this file type”

    先说解决方案: 在项目中找到build,找到webpack.base.conf.js 将vux给出的解决方案代码拷贝出来 const vuxLoader = require('vux-loader') ...

  10. CentOS6.3上搭建expect无交互开发环境

    1.背景 在面向shell编程时对于需要交互的场合则必须通过人工来干预,而对于这种方式是违反无人职守的原则:现在expect就解决了这个问题, Expect是一个免费的编程工具语言,用来实现自动和交互 ...