CSSOM

Written by with ♥ on in 浏览器

CSSOM 是 CSS 的对象模型。

与 DOM 的区别是 DOM 表示元素的语义,CSSOM 表示元素的表现形式,对应渲染进程绘制元素过程的产物,具体某个元素长什么样子。例如,在一个 DOM 元素上并没有直接获取元素渲染信息的 API(element.width、element.hight),因为这些都被划入到 CSSOM 模块中。

设计浏览器的大佬做了逻辑上的拆分,在 W3C 标准中,CSSOM 包含两个部分:

  1. CSSOM:描述样式表和规则等 CSS 模型的部分
  2. CSSOM View:元素视图相关的 View 部分

CSSOM

在 HTML 中引入样式我们会使用 <style><link> 标签,从 DOM 的角度看,这两个标签也是两个 DOM 节点,自然遵循 DOM 的操作规则(访问属性、内容)。

<style title="Hello">
a {
  color:red;
}
</style>

<link rel="stylesheet" title="x" href="data:text/css,p%7Bcolor:blue%7D">

假设我们通过 DOM API 取到 CSS 的文本内容,学过编译原理的都知道,从字符串文本到可执行的代码需要经过词法分析等一系列的步骤,所有,这件事浏览器一定会帮你做,So,这就是 CSSOM(浏览器解析好了,封装成 CSSOM 给你用)。

document.styleSheets 属性就是文档中的所有样式表,styleSheets 是个数组,每个元素对应一个 <style><link> 标签:

  • length 属性表示文档中的样式表数量
  • __proto__ 指向原型对象 CSSStyleSheet 有一些操作样式表的方法(没有创建的方法)
    • insertRule() 插入 CSS,Example:document.styleSheets[0].insertRule("p { color:pink; }", 0)(会直接插入到 <style> 标签中)
    • removeRule() 删除某个 CSS,Example:document.styleSheets[0].removeRule(0)

获取某个样式表中的特定规则列表:document.styleSheets[0].cssRules

cssRules 又是一个数组,CSS 有几种不同的语法,因此数组中的元素有好多种类型:

  • CSSStyleRule
  • CSSCharsetRule ….

这里只介绍最常用的 CSSStyleRule

  • selectorText 属性表示一个规则的选择器部分(是个字符串,显然设计的时候偷懒了,没有抽象对应的对象)
  • style 属性表示一个规则的样式部分(对应 CSSStyleDeclaration 对象),修改它就可以修改对应的 CSS 规则

CSSOM 还提供一个重要的方法,获取一个元素最终通过 CSS 计算得到的属性 window.getComputedStyle(elt, pseudoElt) 其中第一个参数就是我们要获取属性的元素,第二个参数是可选的,在这里选择伪元素。

CSSOM View

CSSOM View 这部分可以看成是 DOM API 的扩展,添加了显示相关的功能,分为三个部分:

Window API

操作浏览器窗口的位置、尺寸等:

  • moveTo(x, y) 窗口移动到屏幕的特定坐标
  • moveBy(x, y) 窗口移动特定距离
  • resizeTo(x, y) 改变窗口大小到特定尺寸
  • resizeBy(x, y) 改变窗口大小特定尺寸(增加或减少)

Scroll API

滚动 API。

在 PC 时代,浏览器可视区域(视口)的滚动和内部元素的滚动关系是比较模糊的,但在移动端越来越重要的今天,两者必须分开看待,性能和行为都有差别,分为全局和元素上的 API。

视口 Scroll

浏览器可视区域称为视口,其滚动行为由 window 对象上的一组 API 控制:

  • scrollX 属性,表示 X 方向上的当前滚动距离
  • scrollY 属性,表示 Y 方向上的当前滚动距离
  • scroll(x, y) 函数,页面滚动到特定的位置,Example:window.scroll(0, 0) 实现回到顶部的效果
  • scrollBy(x, y) 函数,页面滚动特定的距离

监听视口滚动事件:

document.addEventListener("scroll", function(event){
  //......
})

// 视口移动到特点的位置
window.scrollTo({
  left: 0,
  top: 0
});

可滚动元素 Scroll

滚动元素就是内部的子元素显式不完,产生滚动条的元素和可滚动区域的元素。

  • scrollTop 表示 Y 方向上的当前滚动距离
  • scrollLeft 表示 X 方向上的当前滚动距离
  • scrollWidth 表示元素内部的滚动内容的宽度,一般会大于等于元素的宽度
  • scrollHeight 表示元素内部的滚动内容的高度,一般会大于等于元素的高度
  • scroll(x, y) 使得元素滚动到特定的位置
  • scrollBy(x, y) 元素滚动特定的位置
  • scrollIntoView(arg) 滚动元素所在的父元素,使得元素滚动到可见区域,可以通过 arg 指定滚到中间、开始或者就近

可滚动的元素支持 scroll 事件:

element.addEventListener("scroll", function(event){
  //......
})

布局 API

这是整个 CSSOM View 中最常用的部分,同样分成全局 API 和元素 API。

全局尺寸信息

window 对象上提供一些全局的尺寸信息:

  • window.innerHeight、window.innerWidth 对应视口大小
  • window.outerWidth, window.outerHeight 对应浏览器窗口大小(很多浏览器没实现)
  • window.devicePixelRatio 这个属性比较重要,表示物理像素和 CSS 像素单位的倍率关系,retain 屏的值时 2,还有一些值为 3 的 Android 屏
  • window.screen(屏幕尺寸相关的信息)
    • window.screen.width、window.screen.height 设备的屏幕尺寸
    • window.screen.availWidth、window.screen.availHeight 设备屏幕的可渲染区域尺寸,有些 Android 机器会把屏幕的一部分预留做固定按钮(一般浏览器不会实现这么细致)
    • window.screen.colorDepth、window.screen.pixelDepth 固定值 24,暂时没什么作用

最重要的是 window.innerHeight、window.innerWidthwindow.devicePixelRatio 这三个属性,因为前端工作主要是跟视口打交道。

元素的布局信息

通过这些 API 我们能实现获取元素的宽高(准确的说是盒的宽高,有些元素会产生多个盒,盒有宽高,元素没有)。

  • getClientRects() 返回一个列表,里面包含元素产生的每一个盒所占据的客户端矩形区域(每个矩形区域可用 x,y,width,height)来获取位置和尺寸
  • getBoundingClientRect():返回元素产生的所有盒包裹的矩形区域(不包含 margin),注意:获取的区域会包括当 overflow 为 visible 时的子元素区域

这两个 API 获取的矩形区域都是相对于视口的坐标,也就是说,当屏幕滚动时坐标会受到影响,下面的代码可以获取到相对文档的坐标:

var offsetX = document.documentElement.getBoundingClientRect().x - element.getBoundingClientRect().x;

计算元素坐标

应用

获取元素的 width 和 height

// 返回对象,包含 width 和 height
ele.getBoundingClientRect()