关于js中的offsetWidth、clientWidth、scrollWidth等一系列属性及其方法一直都傻傻分不清,这里就来总结一下这些方法的用法和含义。
注意: 下面元素属性和元素方法都通过 elem.属性 或 elem.方法的方式使用的,window属性通过 window.属性的方式使用,document属性则通过document调用。

  1. <script>
  2. /*
  3. ------------------元素视图属性
  4. * offsetWidth 水平方向 width + 左右padding + 左右border-width
  5. * offsetHeight 垂直方向 height + 上下padding + 上下border-width
  6. *
  7. * clientWidth 水平方向 width + 左右padding
  8. * clientHeight 垂直方向 height + 上下padding
  9. *
  10. * offsetTop 获取当前元素到 定位父节点 的top方向的距离
  11. * offsetLeft 获取当前元素到 定位父节点 的left方向的距离
  12. *
  13. * scrollWidth 元素内容真实的宽度,内容不超出盒子高度时为盒子的clientWidth
  14. * scrollHeight 元素内容真实的高度,内容不超出盒子高度时为盒子的clientHeight
  15. --------------- 元素视图属性结束
  16. *
  17. ------------------- Window视图属性(低版本IE浏览器[<IE9]不支持)
  18. * innerWidth 浏览器窗口可视区宽度(不包括浏览器控制台、菜单栏、工具栏)
  19. * innerHeight 浏览器窗口可视区高度(不包括浏览器控制台、菜单栏、工具栏)
  20. * ***** Window视图属性结束
  21. *
  22. ------------------Document文档视图
  23. * (低版本IE的innerWidth、innerHeight的代替方案)
  24. * document.documentElement.clientWidth 浏览器窗口可视区宽度(不包括浏览器控制台、菜单栏、工具栏、滚动条)
  25. * document.documentElement.clientHeight 浏览器窗口可视区高度(不包括浏览器控制台、菜单栏、工具栏、滚动条)
  26. *
  27. * document.documentElement.offsetHeight 获取整个文档的高度(包含body的margin)
  28. * document.body.offsetHeight 获取整个文档的高度(不包含body的margin)
  29. *
  30. * document.documentElement.scrollTop 返回文档的滚动top方向的距离(当窗口发生滚动时值改变)
  31. * document.documentElement.scrollLeft 返回文档的滚动left方向的距离(当窗口发生滚动时值改变)
  32. --------------------------Document文档视图结束
  33. *
  34. -----------------元素方法
  35. * 1. getBoundingClientRect() 获取元素到body
  36. * bottom: 元素底边(包括border)到可视区最顶部的距离
  37. * left: 元素最左边(不包括border)到可视区最左边的距离
  38. * right: 元素最右边(包括border)到可视区最左边的距离
  39. * top: 元素顶边(不包括border)到可视区最顶部的距离
  40. * height: 元素的offsetHeight
  41. * width: 元素的offsetWidth
  42. * x: 元素左上角的x坐标
  43. * y: 元素左上角的y坐标
  44. *
  45. * 2. scrollIntoView() 让元素滚动到可视区
  46. *
  47. -----------------元素方法结束
  48. *
  49. */
  50. </script>
  • clientLeft,clientTop
    表示内容区域的左上角相对于整个元素左上角的位置(包括边框)。(取决于边框的像数值?)
  • clientWidth,clientHeight
    内容区域的宽高,不包括边框宽度值。
  • clientX、clientY
    点击位置距离当前body可视区域的x,y坐标
  • offsetLeft,offsetTop
    相对于最近的祖先定位元素。
  • offsetParent
    某元素的父元素 例如:this.offsetParent.tagName.toLowerCase() 得到body…
  • offsetWidth,offsetHeight
    整个元素的尺寸(不包括因为滚动条变宽的宽度,包括滚动条的宽度和高度)
  • offsetX, offsetY
    相对于带有定位的父盒子的x,y坐标
  • scrollLeft,scrollTop
    元素滚动的距离大小
  • scrollWidth,scrollHeight
    整个内容区域的宽度(包括需拉动滚动条隐藏起来的那些部分) scrollWidth = scrollTop+clientWidth
  • pageX、pageY
    对于整个页面来说,包括了被卷去的body部分的长度
  • screenX、screenY
    点击位置距离当前电脑屏幕的x,y坐标
  • x、y
    和screenX、screenY一样

    示例元素

    作为演示属性的示例元素,我们将使用下面给出的元素:
    1. <div id="example">
    2. ...Text...
    3. </div>
    4. <style>
    5. #example {
    6. width: 300px;
    7. height: 200px;
    8. border: 25px solid #E8C48F;
    9. padding: 20px;
    10. overflow: auto;
    11. }
    12. </style>
    它有边框(border),内边距(padding)和滚动(scrolling)等全套功能。但没有外边距(margin),因为它们不是元素本身的一部分,并且它们没什么特殊的属性。
    这个元素看起来就像这样:
    image.png

注意滚动条

上图演示了元素具有滚动条这种最复杂的情况。一些浏览器(并非全部)通过从内容(上面标记为 “content width”)中获取空间来为滚动条保留空间。 因此,如果没有滚动条,内容宽度将是 300 px,但是如果滚动条宽度是 16px(不同的设备和浏览器,滚动条的宽度可能有所不同),那么还剩下 300 - 16 = 284px,我们应该考虑到这一点。这就是为什么本章的例子总是假设有滚动条。如果没有滚动条,一些计算会更简单。

文本可能会溢出到 padding-bottom

在我们的插图中的 padding 中通常显示为空,但是如果元素中有很多文本,并且溢出了,那么浏览器会在 padding-bottom 处显示“溢出”文本,这是正常现象。

几何

这是带有几何属性的整体图片:
image.png
这些属性的值在技术上讲是数字,但这些数字其实是“像素(pixel)”,因此它们是像素测量值。
让我们从元素外部开始探索属性。

offsetParent,offsetLeft/Top

这些属性很少使用,但它们仍然是“最外面”的几何属性,所以我们将从它们开始。
offsetParent 是最接近的祖先(ancestor),在浏览器渲染期间,它被用于计算坐标。
最近的祖先为下列之一:

  1. CSS 定位的(positionabsoluterelativefixed),
  2. <td><th><table>
  3. <body>

属性 offsetLeft/offsetTop 提供相对于 offsetParent 左上角的 x/y 坐标。
在下面这个例子中,内部的 <div><main> 作为 offsetParent,并且 offsetLeft/offsetTop 让它从左上角位移(180):

  1. <main style="position: relative" id="main">
  2. <article>
  3. <div id="example" style="position: absolute; left: 180px; top: 180px">...</div>
  4. </article>
  5. </main>
  6. <script>
  7. alert(example.offsetParent.id); // main
  8. alert(example.offsetLeft); // 180(注意:这是一个数字,不是字符串 "180px")
  9. alert(example.offsetTop); // 180
  10. </script>

image.png
有以下几种情况下,offsetParent 的值为 null

  1. 对于未显示的元素(display:none 或者不在文档中)。
  2. 对于 <body><html>
  3. 对于带有 position:fixed 的元素。

    offsetWidth/Height

    现在,让我们继续关注元素本身。
    这两个属性是最简单的。它们提供了元素的“外部” width/height。或者,换句话说,它的完整大小(包括边框)。
    image.png
    对于我们的示例元素:
  • offsetWidth = 390 — 外部宽度(width),可以计算为内部 CSS-width(300px)加上 padding(2 * 20px)和 border(2 * 25px)。
  • offsetHeight = 290 — 外部高度(height)。

对于未显示的元素,几何属性为 0/null
仅针对显示的元素计算几何属性。
如果一个元素(或其任何祖先)具有 display:none 或不在文档中,则所有几何属性均为零(或 offsetParentnull)。
例如,当我们创建了一个元素,但尚未将其插入文档中,或者它(或它的祖先)具有 display:none 时,offsetParentnull,并且 offsetWidthoffsetHeight0
我们可以用它来检查一个元素是否被隐藏,像这样:

  1. function isHidden(elem) {
  2. return !elem.offsetWidth && !elem.offsetHeight;
  3. }

请注意,对于屏幕上显示,但大小为零的元素(例如空的 <div>),它们的 isHidden 返回 true

clientTop/Left

在元素内部,我们有边框(border)。
为了测量它们,可以使用 clientTopclientLeft
在我们的例子中:

  • clientLeft = 25 — 左边框宽度
  • clientTop = 25 — 上边框宽度

image.png
……但准确地说 — 这些属性不是边框的 width/height,而是内侧与外侧的相对坐标。
有什么区别?
当文档从右到左显示(操作系统为阿拉伯语或希伯来语)时,影响就显现出来了。此时滚动条不在右边,而是在左边,此时 clientLeft 则包含了滚动条的宽度。
在这种情况下,clientLeft 的值将不是 25,而是加上滚动条的宽度 25 + 16 = 41
这是希伯来语的例子:

image.png

clientWidth/Height

这些属性提供了元素边框内区域的大小。
它们包括了 “content width” 和 “padding”,但不包括滚动条宽度(scrollbar):
image.png

在上图中,我们首先考虑 clientHeight
这里没有水平滚动条,所以它恰好是 border 内的总和:CSS-width 200px 加上顶部和底部的 padding(2 * 20px),总计 240px
现在 clientWidth — 这里的 “content width” 不是 300px,而是 284px,因为被滚动条占用了 16px。所以加起来就是 284px 加上左侧和右侧的 padding,总计 324px
image.png
如果这里没有 padding,那么 clientWidth/Height 代表的就是内容区域,即 border 和 scrollbar(如果有)内的区域。
因此,当没有 padding 时,我们可以使用 clientWidth/clientHeight 来获取内容区域的大小。

scrollWidth/Height

这些属性就像 clientWidth/clientHeight,但它们还包括滚动出(隐藏)的部分:
image.png
在上图中:

  • scrollHeight = 723 — 是内容区域的完整内部高度,包括滚动出的部分。
  • scrollWidth = 324 — 是完整的内部宽度,这里我们没有水平滚动,因此它等于 clientWidth

我们可以使用这些属性将元素展开(expand)到整个 width/height。
像这样:
// 将元素展开(expand)到完整的内容高度
element.style.height = ${element.scrollHeight}px;

image.pngimage.png

scrollLeft/scrollTop

属性 scrollLeft/scrollTop 是元素的隐藏、滚动部分的 width/height。
在下图中,我们可以看到带有垂直滚动块的 scrollHeightscrollTop
image.png
换句话说,scrollTop 就是“已经滚动了多少”。
scrollLeft/scrollTop 是可修改的
大多数几何属性是只读的,但是 scrollLeft/scrollTop 是可修改的,并且浏览器会滚动该元素。
如果你点击下面的元素,则会执行代码 elem.scrollTop += 10。这使得元素内容向下滚动 10px

scrollTop 设置为 0 或一个大的值,例如 1e9,将会使元素滚动到顶部/底部。

不要从 CSS 中获取 width/height

我们刚刚介绍了 DOM 元素的几何属性,它们可用于获得宽度、高度和计算距离。
但是,正如我们在 样式和类 一章所知道的那样,我们可以使用 getComputedStyle 来读取 CSS-width 和 height。
那为什么不像这样用 getComputedStyle 读取元素的 width 呢?

  1. let elem = document.body;
  2. alert( getComputedStyle(elem).width ); // 显示 elem 的 CSS width

为什么我们应该使用几何属性呢?这里有两个原因:

  1. 首先,CSS width/height 取决于另一个属性:box-sizing,它定义了“什么是” CSS 宽度和高度。出于 CSS 的目的而对 box-sizing 进行的更改可能会破坏此类 JavaScript 操作。
  2. 其次,CSS 的 width/height 可能是 auto,例如内联(inline)元素:

    1. <span id="elem">Hello!</span>
    2. <script>
    3. alert( getComputedStyle(elem).width ); // auto
    4. </script>
  3. 从 CSS 的观点来看,width:auto 是完全正常的,但在 JavaScript 中,我们需要一个确切的 px 大小,以便我们在计算中使用它。因此,这里的 CSS 宽度没什么用。

还有另一个原因:滚动条。有时,在没有滚动条的情况下代码工作正常,当出现滚动条时,代码就出现了 bug,因为在某些浏览器中,滚动条会占用内容的空间。因此,可用于内容的实际宽度小于 CSS 宽度。而 clientWidth/clientHeight 则会考虑到这一点。
……但是,使用 getComputedStyle(elem).width 时,情况就不同了。某些浏览器(例如 Chrome)返回的是实际内部宽度减去滚动条宽度,而某些浏览器(例如 Firefox)返回的是 CSS 宽度(忽略了滚动条)。这种跨浏览器的差异是不使用 getComputedStyle 而依靠几何属性的原因。
如果你的浏览器保留了滚动条的空间(大多数 Windows 中的浏览器),那么你可以在下面测试它。
image.png
带有文本的元素具有 width:300px
在桌面 Windows 操作系统上,Firefox、Chrome、Edgy 都为滚动条保留了空间。但 Firefox 显示的是 300px,而 Chrome 和 Edgy 显示较少。这是因为 Firefox 返回 CSS 宽度,其他浏览器返回“真实”宽度。
请注意,所描述的差异只是关于从 JavaScript 读取的 getComputedStyle(...).width,而视觉上看,一切都是正确的。

总结

元素具有以下几何属性:

  • offsetParent — 是最接近的 CSS 定位的祖先,或者是 tdthtablebody
  • offsetLeft/offsetTop — 是相对于 offsetParent 的左上角边缘的坐标。
  • offsetWidth/offsetHeight — 元素的“外部” width/height,边框(border)尺寸计算在内。
  • clientLeft/clientTop — 从元素左上角外角到左上角内角的距离。对于从左到右显示内容的操作系统来说,它们始终是左侧/顶部 border 的宽度。而对于从右到左显示内容的操作系统来说,垂直滚动条在左边,所以 clientLeft 也包括滚动条的宽度。
  • clientWidth/clientHeight — 内容的 width/height,包括 padding,但不包括滚动条(scrollbar)。
  • scrollWidth/scrollHeight — 内容的 width/height,就像 clientWidth/clientHeight 一样,但还包括元素的滚动出的不可见的部分。
  • scrollLeft/scrollTop — 从元素的左上角开始,滚动出元素的上半部分的 width/height。

除了 scrollLeft/scrollTop 外,所有属性都是只读的。如果我们修改 scrollLeft/scrollTop,浏览器会滚动对应的元素。