判断元素是否在可视区域内

# 判断元素是否在可视区域内

可视区域即我们浏览网页的设备肉眼可见的区域. 常见的应用场景有:

  • 图片的懒加载
  • 列表的无线滚动
  • 计算广告元素的曝光情况
  • 可点击链接的预加载

实现的方式常见的有三种:

# Element.getBoundingClientRect()

getBoundingClientRect 返回一个 DOMRect (opens new window) 对象, 提供了元素的大小及其想对视口的位置.

示意图如下:

Element.getBoundingClientRect

如果一个元素在视窗之内的话,那么它一定满足下面四个条件:

  • top 大于等于 0
  • left 大于等于 0
  • bottom 小于等于视窗高
  • right 小于等于视窗宽度
点击查看代码
/**
 * 判断元素是否在可视区域内 (getBoundingClientRect) 
 * 如果一个元素在视窗之内的话,那么它一定满足下面四个条件:
 * - top 大于等于 
 * - left 大于等于 
 * - bottom 小于等于视窗高
 * - right 小于等于视窗宽度
 * 
 * @param {HTMLElement} element 
 * @returns {boolean}
 */
function inViewport(element) {
  const viewWidth = window.innerWidth || document.documentElement.clientWidth
  const viewHeight = window.innerHeight || document.documentElement.clientHeight

  const {
    top, right, bottom, left, 
  } = element.getBoundingClientRect()

  return (
    top >= 0 &&
    left >= 0 &&
    right <= viewWidth &&
    bottom <= viewHeight
  )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# IntersctionObserver

Intersection Observer API 会注册一个回调函数, 每当被监视的元素进入或者退出另外一个元素时 (或者 viewport),或者两个元素的相交部分大小发生变化时, 该回调方法会被触发执行

点击查看代码
/**
 * 判断元素是否在可视区域内(IntersectionObserver)
 * MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver
 * @param {HTMLElement} element
 */
function inViewport(element) {
  const observeCallback = (entries) => {
    if(entries[0].isIntersecting) {
      console.log('Yes, in viewport.')
    } else {
      console.log('No, not in viewport.')
    }
  }
  const observer = new IntersectionObserver(observeCallback, { threshold: 1.0 })
  observer.observe(element)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# offsetTop/scrollTop

这种方式受布局影响, 有些不太准确

点击查看代码
/**
 * 判断元素是否在可视区域内(offsetTop scrollTop)
 * 不太准确, 受布局的影响会导致判断偏差
 * 公式: el.offsetTop - document.documentElement.scrollTop <= viewPortHeight
 * @param {HTMLElement} element 
 */
function inViewport(element) {
  const viewportHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
  const elOffsetTop = element.offsetTop
  const docScrollTop = document.documentElement.scrollTop
  const top = elOffsetTop - docScrollTop
  return top <= viewportHeight
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 参考资料