使用 CSSOM 测量 DOM 元素

Liu Bowen

Liu Bowen / 2018, 九月, 01

本文根据 W3C CSSOM viewformal, draftMDN WEB DOCS 中一些测量元素的 CSSOM 属性归纳总结出一些常见基础的元素测量方案。

先决条件

Note that visibility: hidden is different from display: none. The former makes the element invisible, but the element still occupies space in the layout (that is, it's rendered as an empty box), whereas the latter (display: none) removes the element entirely from the render tree such that the element is invisible and is not part of the layout.

基于 Google web fundamental 中关于渲染树的解析,当元素的 display 属性为 none 时,该元素将完全从 render tree 中移除,并且该元素是不可见,且 不是 布局的一部分。

This value causes an element to not appear in the formatting structure (i.e., in visual media the element generates no boxes and has no effect on layout).

W3C CSS 标准中,对 displaynone描述,当该属性为 none 时,元素是不可见的,且 不生成 任何的盒模型,且 对布局产生影响最新草案

那么,据上文的标准理论,本文所述的 “存在与元素相关联的 CSS layout box” 是指元素存在于当前 render tree 中,即 元素及其祖先元素display 的值不为 none

scroll~ 系列属性

scroll~ 系列属性可用于测量一个元素的 scroll area 的大小和 scroll area 相对于自身可见区域的位置信息。

scroll~ series只读含义
scrollTopMDN, W3C——元素的滚动区域相对于最近可见区域的 padding edgey 值(若以 padding edgey=0 位置)
scrollLeftMDN, W3C——元素的滚动区域相对于最近可见区域的 padding edgex 值(若以 padding edgex=0 位置)
scrollWidthMDN, W3C✔️元素滚动区域#的宽度
scrollHeightMDN, W3C✔️元素滚动区域#的高度

scrollTop

适用于测量 滚动区域 相对于 可视区域(Its topmost visible content)的 顶部 padding edge的距离。

Getter

Return the y-coordinate of the scrolling area at the alignment point with the top of the padding edge of the element# 9..

返回元素的顶部 padding edge 和滚动区域的对齐点的 y 坐标值(若以 padding edgey=0 位置)。

展示一个元素 垂直方向已发生 滚动的距离的像素值。一个元素的 scrollTop 的值来源于该元素 content 区域到最顶级的 可见 内容区域(即滚动容器)的 padding edge#.9 的距离。当一个元素的内容区域 content 未生成垂直滚动条时,那么该元素的 scrollTop 的值恒为 0,具体的计算方式可见 W3C CSS view

W3C CSS box model 规范中,明确指出一个元素的 padding edge 包围着一个元素盒子的 padding。当设置给定的一边 padding0 时,此时该边的 padding edge 与元素盒子的 content edge是一致的。四个方向的 padding edge 形成了一个 padding box

那么通俗地理解,padding edge 指的是 padding box 的四个边界

Setter

直接赋值给元素的 scrollTop 可实现移动该元素的 垂直 滚动条的位置。特别地,该值不能为负值,否则为 0,且不能超出内容区域可滚动的最大值,否则为最大值。

在设置元素的 scrollTopMDN, W3C 后,在未指定 scroll behaviorMDN, W3C 的情况下,将执行 scroll behavior: auto(即立即滚动,而非平滑滚动),并滚动到 [element.scrollLeft, element.scroll.Top] 点。

具体的实现方式可见于 W3C CSS view - scrollTopset 部分。

js
// 在当前页面生成滚动条时,即根元素的 `content` 区域生成垂直滚动条时,scrollTop 将变化
document.documentElement.scrollTop // 将展示当前页面滚动条的移动位置

// 由于历史原因存在以下兼容性解决方案
const nowLocation =
  document.documentElement.scrollTop ||
  window.pageYOffset || // window.scrollY 的别名
  document.body.scrollTop

scrollLeft

适用于测量 滚动区域 相对于 可视区域(Its topmost visible content)的 左边 padding edge的距离。

Getter

Return the x-coordinate of the scrolling area at the alignment point with the left of the padding edge of the element# 9..

返回元素的左边的 padding edge 与(子级)滚动区域的对齐点处的 x 坐标值(若以 padding edgex=0 位置)。

展示一个元素 水平方向已发生 滚动的距离的像素值。一个元素的 scrollLeft 的值来源于该元素 content 区域到最顶级的 可见 内容区域(即滚动容器)的 padding edge#.9 的距离。当一个元素的内容区域 content 未生成水平滚动条时,那么该元素的 scrollLeft 的值恒为 0,具体的计算方式可见 W3C CSS view

Setter

直接赋值给元素的 scrollLeftMDN, W3C 可实现移动该元素的 水平 滚动条的位置。特别地,该值不能为负值,否则为 0,且不能超出内容区域可滚动的最大值,否则为最大值。

在设置元素的 scrollLeftMDN, W3C 后,在未指定 scroll behaviorMDN, W3C 的情况下,将执行 scroll behavior: auto(即立即滚动,而非平滑滚动),并滚动到 [element.scrollLeft, element.scroll.Top] 点。

scrollWidth

Return the width of the element’s scrolling area.

返回 滚动区域# 的宽度。

只读 属性。描述了当前元素的内容 content box 区域的宽度,其中包含因溢出屏幕不可见的内容区域。

scrollHeight 相似,在不存在水平滚动条时,当前元素的 scrollWidth 等于 clientWidth。且 scrollHeight 的值为一个 Math.round 的四舍五入近似值。

scrollHeight

Return the height of the element’s scrolling area.

返回 滚动区域# 高度。

只读 属性。描述了一个元素的内容区域的测量高度值。被测量的内容区域 包含 因为溢出屏幕而不可见的内容区域。

scrollHeight 等于在没有垂直滚动条时,元素在 viewport 内为了适应所有内容所需的最小高度。该高度以同样的方式被 clientHeight 测量:它包含了元素的 padding ,但不包含 bordermargin 或者水平滚动条(如果存在的话)。它同样包含了伪元素的高度,如 ::before::after。 如果元素的内容区域可以被自动适应的情况下,且没有出现垂直滚动条时,scrollHeight 将等于 clientHeight

This property will round the value to an integer. If you need a fractional value, use Element.getBoundingClientRect().

注: 该参数始终返回一个 Math.round 四舍五入取整的整数。如果需要小数位的值,可使用 Element.getBoundingClientRect()

client~ 系列属性

  1. clientTopclientLeft 一般指 包含 滚动条宽高的指定边的 border 的宽度。

  2. clientWidthclientHeight 主要用于测量元素的 不包含 滚动条的 padding box 区域的宽高。

client~ series只读含义
clientTopMDN, W3C✔️元素的 顶端 border 宽度加上元素的 顶端padding edge顶端border edge 顶端之间渲染出的所有滚动条的 高度
clientLeftMDN, W3C✔️元素的 左侧 border 宽度加上元素 左侧 padding edge左侧 border edge 之间所有滚动条的 宽度
clientWidthMDN, W3C✔️不包括垂直滚动条的 padding box 区域宽度,或没有发生水平滚动的 scrollWidth 宽度
clientHeightMDN, W3C✔️不包括水平滚动条的 padding box 区域高度,或没有发生垂直滚动的 scrollHeight 高度

clientTop

Return the computed value of the border-top-width property plus the height of any scrollbar rendered between the top padding edge and the top border edge, ignoring any transforms that apply to the element and its ancestors.

只读 属性。返回 border-top-width 的计算值加上元素的顶端的 padding edge 和顶端的 border edge 之间渲染出的所有滚动条的 高度。并且 忽略 元素及其祖先元素的所有 transforms 效果。

即在没有滚动条的情况下,clientTop 是元素顶部 border宽度。该属性只返回一个 四舍五入 的整数值,若需要一个精确值,应该使用 element.getBoundingClientRect()

clientLeft

Return the computed value of the border-left-width property plus the width of any scrollbar rendered between the left padding edge and the left border edge, ignoring any transforms that apply to the element and its ancestors.

只读 属性。返回元素的 border-left-width 计算值加上在元素的左侧 padding edge 和 左侧 border-edge 之间渲染出的所有垂直滚动条的 宽度忽略 所有应用在当前元素及其它的祖先元素上的 transforms 效果。

layout.scrollbar.side preference 设置(滚动条偏好)值为 13,或当 text-direction 的值设置为 RTL 时,元素的垂直滚动条将布局在元素的 左侧,这将影响到元素的 clientLeftMDN, W3C 的计算。

clientWidth

  1. If the element has no associated CSS layout box or if the CSS layout box is inline, return zero.
  2. If the element is the root element and the element’s node document is not in quirks mode, or if the element is the HTML body element and the element’s node document is in quirks mode, return the viewport width excluding the size of a rendered scroll bar (if any).
  3. Return the width of the padding edge excluding the width of any rendered scrollbar between the padding edge and the border edge, ignoring any transforms that apply to the element and its ancestors.

只读 属性。

  1. 当一个元素没有相关联的 CSS layout boxCSS layout box 是行内盒模型时,返回 0
  2. 当元素是根元素并且元素的 node document 不在 怪异模式 下,或元素是 <body> 元素且元素在 怪异模式 下,返回不包含滚动条宽度(如有)的视口宽度。
  3. 返回不包含在 padding edgeborder edge 之间渲染出的滚动条的宽度的 padding edge 所包围形成的区域的宽度,并且忽略应用在元素及元素祖先上的任意 transforms 效果。

可使用以下伪代码公式计算一个元素的 clientWidthMDN, W3C

js
// 伪代码
const clientWidth =
  element.height +
  element.paddingLeft +
  element.paddingRight -
  verticalScrollbarWidth

clientHeight

Return the height of the padding edge excluding the height of any rendered scrollbar between the padding edge and the border edge, ignoring any transforms that apply to the element and its ancestors.

只读 属性。在没有 CSS layout boxesinline layout boxes 的情况下,对于元素来说,该值为 0 。否则,等于元素的 inner height 的像素值。其中 包含 padding不包含 bordermargin 或 水平滚动条。

可通过以下伪代码公式计算元素的 clientHeightMDN, W3C

js
// 伪代码
const clientHeight =
  element.height +
  element.paddingLeft +
  element.paddingRight -
  horizontalScrollbarWidth

This property will round the value to an integer. If you need a fractional value, use Element.getBoundingClientRect().

注: 该属性始终返回一个 Math.round 四舍五入取整的整数。如果需要小数位的值,可使用 Element.getBoundingClientRect()

offset~ 系列

offset~ series只读含义
offsetParentMDN, W3C✔️元素的最近包含该元素的定位元素的对象引用
offsetTopMDN, W3C✔️元素距离最近 offsetParent 的顶端的距离
offsetLeftMDN, W3C✔️当前元素的 左上角border edge 顶点 相对于 offsetParent 节点左侧 border edge 顶点偏移 offset 的像素
offsetWidthMDN, W3C✔️描述任意具有相关联 CSS layout box 元素的第一个 CSS layout boxborder edge 形成的区域的 宽度,其中,不包含任意 伪元素 的宽度
offsetHeightMDN, W3C✔️描述任意具有相关联的 CSS layout box 元素的第一个 CSS layout boxborder edge 形成的区域的 高度,其中,不包含任意的 伪元素 的高度

块级元素的 offset~ 属性

对于块级元素来说,offsetTopoffsetLeftoffsetWidthoffsetHeight 描述了一个元素的 border box 相对于 offsetParent 的偏移量。

行内元素的 offset~ 属性

对于存在换行 (wrap) 的行内元素来说。offsetTopoffsetLeft 描述了与元素相关联的第一个 CSS layout boxborder edge 左上角顶点相对于 offsetParentyx 轴偏移量,而 offsetWidthoffsetHeight 描述的是 bounding border box 的尺寸(整数值,通过 Element.getBoundingClientRect() 获取精确数值)。

因此,一个行内元素盒模型的 offsetTopoffsetLeftoffsetWidthoffsetHeightlefttopwidthheight 并不会 包含换行的 CSS layout box 部分。

offsetParent

只读 属性。返回最近(指在包含层级上的最近)的定位(该包含元素的 position 值不为默认值)且包含该元素的元素的对象的引用。若元素没有定位,返回最近的 <td><th><table><body>

offsetTopoffsetLeft 都是相对于 offsetParentpadding edge 进行计算的。

元素的 offsetParent 计算方式如下#

  1. 若满足以下任一情况,返回 null 并结束 offsetParent 的算法:

    • 若元素没有相关联的 CSS layout box 时;
    • 若元素是根元素 <html>
    • 若元素是 <body> 元素;
    • 若元素的 position 属性的计算值为 fixed 时。
  2. 涉及 shadow DOM 的计算方式可见于 W3C 原文章节 第二点

offsetTop

只读 属性。展示当前元素相对于 offsetParent 节点顶端 top 的距离。

元素的 offsetTop 计算方法如下:

  1. 若元素的是 <body> 元素或没有相关联的 CSS layout box 时,返回 0 并结束本算法。

  2. 若元素的 offsetParentnull 时,返回相对于 初始包含块 的源点,且与元素关联的 第一个 CSS layout box 的顶部 border edgey 坐标。并忽略所有应用在该元素及其祖先元素上的 transforms 变换,并结束本算法。

    包含块 是一个用于形成与之相关的 大小(sizing)定位(positioning) 的矩形区域(通常情况下指生成它的 box 的子级)。特别地,包含块并不是一个 box(它是一个矩形)。然而,它经常由一个 box 的尺寸推导而来。如果一个包含块的属性被指定,那么就说它们同时也指定了 生成该包含块box 上的值。这些值除非特别指定,否则都是来自于 根元素

    初始包含块 是指根元素的 包含块,对于连续介质(continuous media)来说,它具有视口的尺寸。初始包含块的 direction 属性是和根元素对应属性是一致的。

  3. 返回从相对于 初始包含快 的,且与元素相关联的第一个 CSS layout box 的顶端 border edgey 坐标 减去 与元素的 offsetParent 相关联的第一个 CSS layout box 的顶端 padding edgey 坐标值。

注意:一个由多行盒模型(multiple line boxes)组成的行内元素仅仅计算它的第一个 CSS layout box

offsetLeft

只读 属性。表示当前元素的 左上角border edge 顶点 相对于 offsetParent 节点左侧 border edge 顶点偏移 offset 的像素。

元素 offsetLeft 具体实现如下:

  1. 若元素是 <body> 元素或没有相关联的 CSS layout box 时,返回 0,并结束本算法。

  2. 若元素的 offsetParentnull 时,返回相对于 初始包含快 源点,且与元素相关联的第一个 CSS layout box左侧 border edgex 坐标。忽略所有应用在元素及其祖先元素上的 transforms 效果,并结束本算法。

  3. 返回相对于 初始包含块 源点,且与元素相关联的第一个 CSS layout box左侧 border edgex 坐标 减去 与元素的 offsetParent 相关联的第一个 CSS layout box左侧 border edgex 坐标的结果。忽略所有应用在元素及其祖先元素的 transforms 效果。

    Return the result of subtracting the x-coordinate of the left padding edge of the first CSS layout box associated with the offsetParent of the element from the x-coordinate of the left border edge of the first CSS layout box associated with the element, relative to the initial containing block origin, ignoring any transforms that apply to the element and its ancestors.

offsetWidth

只读 属性。计算方式如下#

  1. 若元素没有相关联的 CSS layout box 那么返回 0 并结束本算法。

  2. 返回元素相关联的 第一个 CSS layout boxborder edge宽度,并忽略所有应用在元素及其祖先元素上的 transforms 效果。

值得注意的是,offsetWidth 并不包含任意的 伪元素 尺寸,但包含任意的渲染出的垂直滚动条(如有)。

js
const offsetWidth =
  bordersWidth + padding + verticalScrollbarWidth - pseudoElementWidth

offsetHeight

只读 属性,计算方式如下:

  1. 如果元素没有相关联的 CSS layout box 时,返回 0,并结束本算法。

  2. 返回与元素相关联的 第一个 CSS layout boxborder edge 形成的区域的 高度,并忽略所有应用在元素及其祖先元素的 transforms 效果。

同样,offsetHeight 的值不包含任意 伪元素 的尺寸,但包含任意渲染出的水平滚动条的 高度

视图总结

  • document 的常规元素的垂直滚动的 CSSOM 解析

set.sh image

  • 包含 transform 变换的 CSSOM 解析

set.sh image

  • document 的垂直滚动的 CSSOM 解析

set.sh image

References