CSS层叠样式表与层叠上下文

235 阅读10分钟

一旦生成了盒子以后,CSS 引擎就需要定位它们以完成布局。下面是定位盒子时所使用的规则:

  1. 普通流:按照次序依次定位每个盒子
    • 在普通流中,盒子会依次放置。
    • 在块格式化上下文中,盒子在垂直方向依次排列;而在行内格式化上下文中,盒子则水平排列。
    • 当 CSS 的 position 属性为 static 或 relative,并且 float 为 none 时,其布局方式为普通流。
  2. 浮动:将盒子从普通流中单独拎出来,将其放到外层盒子的某一边
    • 在浮动定位中,浮动盒子会浮动到当前行的开始或尾部位置。这会导致普通流中的文本及其他内容会“流”到浮动盒子的边缘处,除非元素通过 clear 清除了前面的浮动。
    • 一个盒子的 float 值不为 none,并且其 position 为 static 或 relative 时,该盒子为浮动定位。
  3. 绝对定位:按照绝对位置来定位盒子,其位置根据盒子的包含元素所建立的绝对坐标系来计算,因此绝对定位元素有可能会覆盖其他元素
    • 在绝对定位中,盒子会完全从当前流中移除,并且不会再与其有任何联系,其位置会使用 top、bottom、left 和 right 相对其包含块进行计算。
    • 如果元素的 position 为 absolute 或 fixed,该元素为绝对定位。
    • 对固定位置的元素来说,其包含块为整个视口,该元素相对视口进行绝对定位,因此滚动时元素的位置并不会改变。

层叠样式表

CSS 代表层叠样式表(Cascading Style Sheets),理解第一个词层叠(cascade)很重要——层叠的表现方式是理解 CSS 的关键。

理解层叠

在某些时候,在做一个项目过程中你会发现一些应该产生效果的样式没有生效。通常的原因是你创建了两个应用于同一个元素的规则。与层叠密切相关的概念是优先级(specificity),决定在发生冲突的时候应该使用哪条规则。设计元素样式的规则可能不是期望的规则,因此需要了解这些机制是如何工作的。

样式表层叠——简单的说,就是 CSS 规则的顺序很重要;当应用两条同级别的规则到一个元素的时候,写在后面的就是实际使用的规则。

理解优先级

浏览器是根据优先级来决定当多个规则有不同选择器对应相同的元素的时候需要使用哪个规则。它基本上是一个衡量选择器具体选择哪些区域的尺度:

  • 一个元素选择器不是很具体,则会选择页面上该类型的所有元素,所以它的优先级就会低一些。
  • 一个类选择器稍微具体点,则会选择该页面中有特定 class 属性值的元素,所以它的优先级就要高一点。

有三个因素需要考虑,根据重要性排序如下,后面的更重要:

  1. 资源顺序
  2. 优先级
  3. 重要程度

相互冲突的声明将按以下顺序应用,后一种声明将覆盖前一种声明:

  1. 用户代理样式表中的声明(例如,浏览器的默认样式,在没有设置其他样式时使用)。
  2. 用户样式表中的常规声明(由用户设置的自定义样式)。
  3. 作者样式表中的常规声明(这些是我们 web 开发人员设置的样式)。
  4. 作者样式表中的 !important 声明
  5. 用户样式表中的 !important 声明
  6. 用户代理样式表中的 !important 声明

CSS 动画 (en-US),指使用@keyframes @规则定义状态间的动画。关键帧不参与层叠,意味着在任何时候 CSS 都是取单一的 @keyframes 的值而不会是某几个 @keyframe 的混合。

当有多个满足条件的关键帧时,在最重要的文档里面最后定义的关键帧会被选中,而不会是将它们组合在一起。

同时仍应注意用 @keyframes @规则定义的值会覆盖全部普通值,但会被 !important 的值覆盖。

理解继承

继承也需要在上下文中去理解——一些设置在父元素上的 CSS 属性是可以被子元素继承的,有些则不能。

  • 控制继承:CSS 为控制继承提供了五个特殊的通用属性值。每个 CSS 属性都接收(inherit、initial、unset、 revert或 revert-layer)这些值。
  • 重设所有属性值:CSS 的简写属性 all 可以用于同时将这些继承值中的一个应用于(几乎)所有属性。它的值可以是其中任意一个(inherit、initial、unset 或 revert)。这是一种撤销对样式所做更改的简便方法,以便回到之前已知的起点。

层叠上下文

HTML 元素沿着其相对于用户的一条虚构的 z 轴排开,层叠上下文就是对这些 HTML 元素的一个三维构想。众 HTML 元素基于其元素属性按照优先级顺序占据这个空间。

某些元素的渲染顺序是由其 z-index 的值影响的。这是因为这些元素具有能够使他们形成一个层叠上下文的特殊属性。

文档中的层叠上下文由满足以下任意一个条件的元素形成:

  • 文档根元素(<html>);
  • position 值为 absolute(绝对定位)或 relative(相对定位)且 z-index 值不为 auto 的元素;
  • position 值为 fixed(固定定位)或 sticky(粘滞定位)的元素(沾滞定位适配所有移动设备上的浏览器,但老的桌面浏览器不支持);
  • flex (flex) 容器的子元素,且 z-index 值不为 auto;
  • grid (grid) 容器的子元素,且 z-index 值不为 auto;
  • opacity 属性值小于 1 的元素(参见 the specification for opacity);
  • mix-blend-mode 属性值不为 normal 的元素;
  • 以下任意属性值不为 none 的元素:
    • transform
    • filter
    • backdrop-filter
    • perspective
    • clip-path
    • mask / mask-image / mask-border
  • isolation 属性值为 isolate 的元素;
  • will-change 值设定了任一属性而该属性在 non-initial 值时会创建层叠上下文的元素;
  • contain 属性值为 layout、paint 或包含它们其中之一的合成值(比如 contain: strict、contain: content)的元素。

使用 z-index

在层叠上下文中,子元素同样也按照上面解释的规则进行层叠。重要的是,其子级层叠上下文的 z-index 值只在父级中才有意义。子级层叠上下文被自动视为父级层叠上下文的一个独立单元。

  • 层叠上下文可以包含在其他层叠上下文中,并且一起创建一个层叠上下文的层级。
  • 每个层叠上下文都完全独立于它的兄弟元素:当处理层叠时只考虑子元素。
  • 每个层叠上下文都是自包含的:当一个元素的内容发生层叠后,该元素将被作为整体在父级层叠上下文中按顺序进行层叠。

当你需要指定不同的排列顺序时,只要给元素指定一个 z-index 的数值就可以了。该属性必须是整数 (正负均可),它体现了元素在 z 轴的位置。

当没有元素包含 z-index 属性时,元素按照如下顺序堆叠(从底到顶顺序):

  1. 根元素的背景和边界
  2. 普通流 (无定位) 里的块元素 (没有 position 或者 position:static;) 按 HTML 中的出现顺序堆叠
  3. 定位元素按 HTML 中的出现顺序堆叠

CSS在路上

CSS4

  • :not() 用于将符合规则的元素剔除,将样式规则应用于其他元素上。
  • :matches() 用于匹配所述规则的元素,并应用相应的样式规则,同样不允许嵌套使用。
  • Case-Sensitivity 用于声明某个匹配规则中,对字符串或者某个 value 的匹配不区分大小写。该标志声明于 ] 即右中括号之前,例如 [data-value="case" i],其中的 i 就是 Case-Sensitivity 标识。
  • :dir() 伪类用于匹配符合某个方向性的元素。
  • :lang() 用于匹配声明了 lang=value 的元素,并且可以使用通配符匹配。
  • :any-link 用于匹配带有 href 属性的超链接元素。
  • :current() 匹配时间轴当前的元素,:past() 匹配 :current()元素之前的元素,:future() 则匹配当前时间轴后的所有元素。这里说的时间轴指的是例如 WebVTT。
  • :indeterminate 就是匹配这种不确定状态的 radio 或 checkbox。
  • :default 匹配一组相似元素集合中的默认元素。
  • :in-range:out-of-range 只对有被条件约束的元素起作用。
  • :required:optional 分别匹配带有 required 标识的元素和不带 required 标识的元素。
  • :read-only 匹配不可被编辑的元素,:read-write 则匹配可被编辑的元素。
  • :placeholder-shown 匹配 placeholder 文字显示时的 <input> 元素。
  • :column(selector) 将匹配例如 <table> 中 带有 selector 类名的那一列的所有元素。
  • :nth-column(n) 匹配括号内 n 的计算值的某一列的元素,计算方式是从头开始计算,而 :nth-last-column(n) 则是从后开始计算。
  • :blank,它和 :empty 类似,区别在于 :empty 只能匹配没有任何内容的元素,而 :blank 可以匹配带有 spaces(空格), tabs(缩进符) and segment breaks(段落过段)内容的元素。
  • A >> B 匹配祖先元素为 A 的 B元素,其用法与 A B 一样,与 >, +, ~ 用意一样,不过意义不同。
  • :has(selector) 匹配含有 某些规则 的元素。
  • :drop:drop() 匹配可被放置拖动元素的目标元素,两者区别在于 :drop() 可以匹配一些规则,包括 active, valid, invalid。
  • :where() CSS 伪类函数接受选择器列表作为它的参数,将会选择所有能被该选择器列表中任何一条规则选中的元素。
  • calc() 允许在声明 CSS 属性值时执行一些计算。

Compatibility

CSS4在变量上有了很大的进步,但所能做的还是比较有限。另外,随着Opera和Edge加入Chromium大军,-webkit 前缀的私有属性得到更广泛的支持。

CSS工作原理

rendering.png

当浏览器展示一个文件的时候,它必须兼顾文件的内容和文件的样式信息,下面我们会了解到它处理文件的标准的流程。需要知道的是,下面的步骤是浏览加载网页的简化版本,而且不同的浏览器在处理文件的时候会有不同的方式,但是下面的步骤基本都会出现。

  1. 浏览器载入 HTML 文件(比如从网络上获取)。
  2. 将 HTML 文件转化成一个 DOM(Document Object Model)。
  3. 浏览器会拉取该 HTML 相关的大部分资源,比如嵌入到页面的图片、视频和 CSS 样式。
  4. 浏览器拉取到 CSS 之后会进行解析,根据选择器的不同类型(比如 element、class、id 等等)把他们分到不同的“桶”中。浏览器基于它找到的不同的选择器,将不同的规则(基于选择器的规则,如元素选择器、类选择器、id 选择器等)应用在对应的 DOM 的节点中,并添加节点依赖的样式(这个中间步骤称为渲染树)。
  5. 上述的规则应用于渲染树之后,渲染树会依照应该出现的结构进行布局。
  6. 网页展示在屏幕上(这一步被称为着色)。

当浏览器遇到无法解析的 CSS 代码会发生什么?答案就是浏览器什么也不会做,当浏览器遇到无法解析的选择器的时候,它会直接忽略整个选择器规则,然后解析下一个 CSS 选择器。