各种高度clientHeight/scrollHeight/offsetHeight及应用

1,674 阅读3分钟

总结

  • clientWidth/clientHeight 返回值包含 content + padding,如有滚动条,也不包含滚动条

  • scrollWidth/scrollHeight 返回值包含 content + padding + 溢出内容的尺寸

  • offsetWidth/offsetHeight 返回值包含 content + padding + border,效果与element.getBoundingClientRect() 相同

  • window.innerHeight:获取浏览器视觉视口高度(包括垂直滚动条)。

  • window.outerHeight:获取浏览器窗口外部的高度。 表示整个浏览器窗口的高度,包括侧边栏、窗口镶边和调正窗口大小的边框。

  • window.screen.Height:获取获屏幕取理想视口高度,这个数值是固定的,设备的分辨率/设备像素比

  • window.screen.availHeight:浏览器窗口可用的高度。

  • document.body.clientHeight:获取浏览器布局视口高度,包括内边距,但不包括垂直滚动条、边框和外边距。

scroll client offset

scrollWidth/scrollHeight

element.scrollHeight/scrollWidth = content + padding + 溢出内容的尺寸

表示一个元素内容区域的实际大小, 包括不在页面中的可滚动部分

scrollTop

offsetTop 是当前元素顶部距离最近父元素顶部的距离,和有没有滚动条没有关系

scrollTop 在有滚动条的情况下,为元素可视区域距离元素顶部的像素,也就是已经滚动了多少距离

clientWidth/clientHeight

element.clientHeight/clientWidth = content + padding (如有滚动条,也不包含滚动条)

内容区域的显示大小

对于没有定义CSS或者内联布局盒子的元素为0, 否则,它是元素内部的高度(单位像素),包含内边距,但不包括水平滚动条、边框和外边距。

offsetWidth/offsetHeight

offsetWidth/offsetHeight = content + padding + border

CSS 「标准宽高」,它包含了边框、内边距、元素内容 以及滚动条(如果存在的话)。

box-sizing: border-box + 滚动条 box-sizing: content-box + padding + border + 滚动条

offsetWidth 就表示元素的布局宽高,并不代表实际渲染出来的宽高

比如,当元素的形状发生变化时(放大或缩小等)

element.getBoundingClientRect()

返回值是一个 DOMRect 对象,这个对象是由该元素的 getClientRects() 方法返回的一组矩形的集合,就是该元素的 CSS 边框大小。 返回的结果是包含完整元素的最小矩形,并且拥有 left, top, right, bottom, x, y, width

渲染的宽高请使用 getBoundingClientRect() 方法计算得到真正的 offsetWidth

当元素的形状发生变化时(比如说放大或缩小),offsetWidth 就表示元素的布局宽高,并不代表实际渲染出来的宽高

使用 element.getBoundingClientRect() 只能获取元素的 width / height, 但是这个值是 offsetWidth / offsetHeight ,包括边框的长度。

所以不能获取 clientWidth / clientHeight。

应用

  • 图片懒加载——当图片滚动到可见时才进行加载
  • 内容无限滚动——也就是用户滚动到接近内容底部时直接加载更多,而无需用户操作翻页,给用户一种网页可以无限滚动的错觉
  • 检测广告的曝光情况——为了计算广告收益,需要知道广告元素的曝光情况
  • 在用户看见某个区域时执行任务

频繁触发问题,配合 节流/防抖。

或者 Intersection Observer API可以自动观察元素是否在视口内。(注意兼容性)

判定元素是否滚动到底

element.scrollHeight - element.scrollTop === element.clientHeight

图片懒加载位置判断

以垂直方向滚动为例子

imgEle.getBoundingClientRect().top > 0 && (imgEle.getBoundingClientRect().top < document.body.clientHeight)

在视口区的范围内,没滚动到视口区的上方。

<img data-src={picUrl} src={require('../xxx/default.png')} alt={name} />
let images = el.querySelectorAll('img');
if (!images || !images.length) {
    return;
}
images = [].slice.call(images);
images.forEach(function(imgDom) {
    const { top } = imgDom.getBoundingClientRect()
    if (top > 0 && top < document.body.clientHeight) {
        if (imgDom.getAttribute('data-src') == null || imgDom.getAttribute('data-src') === 'done') {
            return
        }
        imgDom.src = imgDom.getAttribute('data-src');
        imgDom.setAttribute('data-src', 'done');
        imgDom.onerror = function() {
            this.src = require('../xxx/default.png');
        }
    }
})

扩展

当容器不滚动但有溢出的子容器时,这些检查可以确定容器能否滚动: window.getComputedStyle(element).overflowY === 'visible' || window.getComputedStyle(element).overflowY !== 'hidden'