彻底搞懂offsetHeight、clientHeight和scrollHeight

295 阅读3分钟

本文基于浏览器元素大小和位置相关的API,总结出 元素内容是否溢出以及元素是否滚动到底的计算方式。

1. API解读

1.1 offsetHeight

详见HTMLElement.offsetHeight - Web API 接口参考 | MDN

offsetHeight = 边框 + 内边距 + 内容高度 + 水平滚动条

  • 可理解为元素可见的总高度

  • 只读属性

  • 单位是像素

  • 取值不精确,会被四舍五入为整数值

1.2 clientHeight

详见Element.clientHeight - Web API 接口参考 | MDN

clientHeight = 内边距 + 内容高度

  • 可理解为元素可见的内部高度

  • 只读属性

  • 单位是像素

  • 取值不精确,会被四舍五入为整数值

1.3 scrollHeight

详见Element.scrollHeight - Web API 接口参考 | MDN

scrollHeight = 内边距 + 内容高度 + 伪元素高度

  • 可理解为元素实际的内部高度(包含因为溢出而不可见的那部分)

  • 只读属性

  • 单位是像素

  • 取值不精确,会被四舍五入为整数值

  • 如果元素的内容不需要垂直滚动条就可以容纳,那么scrollHeight完全等同于clientHeight

1.4 scrollTop

详见 Element.scrollTop - Web API 接口参考 | MDN

  • 可理解为元素在竖直方向上滚动的距离

  • 可以被设置成任何整数

    • 如果被赋的值小于0,就会把scrollTop设置为0
    • 如果被赋的值大于可滚动的最大值,就会把scrollTop设置为可滚动的最大值
  • 单位是像素

  • 取值较为精确,有可能是小数

1.5 getBoundingClientRect()

详见Element.getBoundingClientRect() - Web API 接口参考 | MDN

getBoundingClientRect函数返回一个对象,返回以下8个属性值

  • width:精确的offsetWidth
  • height:精确的offsetHeight
  • x(取值与left相同):元素左边界到视窗左边界的距离
  • y(取值与top相同):元素左边界到视窗上边界的距离
  • right:元素右边界到视窗左边界的距离
  • bottom:元素下边界到视窗上边界的距离

单位都是像素,取值较为精确,有可能是小数

1.6 getComputedStyle()

详见Window.getComputedStyle() - Web API 接口参考 | MDN

getComputedStyle函数返回一个style对象,包含元素经过计算后的CSS属性值,取值较为精确

2. 场景实践

2.1 判断元素在竖直方向溢出

当scrollHeight不等于clientHeight的时候,可以认为元素在竖直方向上溢出

if (element.scrollHeight !== element.clientHeight) {
  console.log('element overflow in vertical direction')
}

2.2 判断元素是否滚动到底

image.png

由上图可以得出:scrollHeight 约等于 scrollTop 加上 clientHeight

由于scrollTop是精确的,而scrollHeight和clientHeight都是四舍五入的

所以判断元素是否滚到底的思路是:判断 scrollHeight 与 scrollTop + clientHeight 的差值是否足够小

我们可以设置一个阈值(例如1px),当差值小于等于1px的时候,就认为元素已经滚动到底部

if (Math.abs(element.scrollHeight - element.scrollTop - element.clientHeight) <= 1 ) {
  console.log('element is totally scrolled')
}

当元素没有水平滚动条时,我们可以获取clientHeight的精确值,从而让计算结果更加精确

  • clientHeight = 内边距 + 内容高度
  • getBoundingClientRect().height = (精确的)offsetHeight = 内边距 + 内容高度 + 边框

因此,我们可以通过getComputedStyle获取元素在垂直方向上的边框宽度和,就可以得出clientHeight的精确值

const elementStyle = window.getComputedStyle(element)
const borderTopWidth = elementStyle.borderTopWidth
const borderBottomWidth = elementStyle.borderBottomWidth
const clientHeight = element.getBoundingClientRect().height - borderTopWidth - borderBottomWidth

if (Math.abs(element.scrollHeight - element.scrollTop - clientHeight) <= 1) {
  console.log('element is totally scrolled')
}