likes
comments
collection
share

如何判断元素是否在可视页面内

作者站长头像
站长
· 阅读数 9
  • 由场景题扯到了如题所示的问题
  • 我: 使用scrollTop, 其他API我知道有但是具体的我忘了.....

目前判断元素出现在可视区域的有三种方案, 我们来逐个突破:

  1. offsetTop, scrollTop
  2. getBoundingClientRect
  3. IntersectionObserver

一、offsetTop + scrollTop

  1. offsetTop: 当前元素相对于其 offsetParent 元素的顶部内边距的距离
    • offsetParent: 指向最近的包含该元素的定位元素或者最近的 tabletdthbody 元素 如何判断元素是否在可视页面内
  • offsetWidth, offsetHeight包括了content, padding, border, 不包括margin
  • clientWidth, clientHeight包括了content, padding 不包括border, margin
  1. scrollTop: 元素的内容顶部到它的视口可见内容的顶部的距离的度量
  2. 实现如下:
  • 由图可知, 考虑纵轴方向的话
  • 要么元素不在可视区域下方, 此时应该满足 el.offsetTop < document.documentElement.scrollTop + document.documentElement.clientHeight 如何判断元素是否在可视页面内
  • 要么元素不在可视区域上方, 此时应该满足 el.offsetTop > document.documentElement.scrollTop - el.offsetHeight 如何判断元素是否在可视页面内
  • 横向方向同理(此处忽略),代码实现如下
const isInView = (el) => {
    const documentScrollTop = document.documentElement.scrollTop;
    const documentClientHeight = document.documentElement.clientHeight;
    if (el.offsetTop > documentScrollTop + documentClientHeight) {
        console.log('元素在可视区域下方');
    } else if (el.offsetTop < documentScrollTop - el.offsetHeight) {
        console.log('元素在可视区域上方');
    } else {
        console.log('在可视区域啦');
    }
}
const scrollEvent = throttle(isInView);
document.addEventListener('scroll', () => {
    scrollEvent(targetNode);
})

如何判断元素是否在可视页面内

二、getBoundingClientRect

  1. 定义:Element.getBoundingClientRect() 返回一个 DOMRect对象,其提供了元素的大小及其相对于视口的位置
  2. 熟悉API
  • 对于该盒子, position left是220, top是100, margin设置了10, border设置了10, padding设置了20, widthheight都是200。 此时打印出来的DOMReact对象如何所示
  • width打印出来为260, 实际上是width + padding + border的值, height同理
  • y打印出来为110, 实际上是top + margin的值, x同理
  • bottom打印出来为370, 实际上是y + width的值(其实就是不算margin-bottom的值)

如何判断元素是否在可视页面内如何判断元素是否在可视页面内

  • 此处为MDN的图
如何判断元素是否在可视页面内
  • 因为他的x, y, bottom, left都是相对于视图窗口而言, 故实现起来也很方便
  • 要让元素不在窗口上方, 则 bottom 应该大于等于 0
  • 要让元素不在窗口下方, 则 y 应该小于等于视图高度
  • 横向同理, top应该大于等于0, x应该小于等于视图宽度
  • 代码如下
const targetNode = document.querySelector('.child');
const isInView = (el) => {
    const documentClientHeight = document.documentElement.clientHeight;
    const { bottom, y } = el.getBoundingClientRect();
    console.log(bottom, y);
    if (bottom < 0) {
        console.log('元素在可视区域上方')
    } else if (y > documentClientHeight) {
        console.log('元素在可视区域下方')
    } else {
        console.log('在可视区域啦')
    }
}
const scrollEvent = throttle(isInView);
document.addEventListener('scroll', () => {
    scrollEvent(targetNode);
})

如何判断元素是否在可视页面内

三、IntersectionObserver

  1. 定义: IntersectionObserver 接口(从属于 Intersection Observer API)提供了一种异步观察目标元素与其祖先元素或顶级文档视口(viewport)交叉状态的方法。其祖先元素或视口被称为根
  2. 当一个 IntersectionObserver 对象被创建时,其被配置为监听根中一段给定比例的可见区域。一旦 IntersectionObserver 被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值
  • 按照我们前面两种方法,其实都需要去频繁调用getBoundingRect(), scrollTop()方法等, 这些方法都会导致回流重绘且都在主线程上运行, 因此频繁触发、调用可能会造成性能问题。这种检测方法极其怪异且不优雅
  • Intersection Observer API 会注册一个回调函数,每当被监视的元素进入或者退出另外一个元素时 (或者 viewport),或者两个元素的相交部分大小发生变化时,该回调方法才会被触发执行
  • 其实就是浏览器提供了对应的方法, 让主线程不需要再为了监听元素相交而辛苦劳作,浏览器会自行优化元素相交管理
  1. 熟悉API
  • 可以通过new IntersectionObserver(callback, options)实例化一个observe对象
    • options中可以通过root属性指定root元素, 通过threshold属性指定交集触发回调的阈值
    let options = {
      root: document.querySelector("#scrollArea"),
      rootMargin: "0px",
      threshold: 1.0,
    };
    
  • 创建一个 observer 后需要给定一个目标元素进行观察
const targetNode = document.querySelector('.child');
const intersectionObserver = new IntersectionObserver((entries) => {
    console.log(entries);
})
intersectionObserver.observe(targetNode);
  • 我们通过intersectionRatio去判断即可, 如果小于等于0, 则说明已经在视野外了 如何判断元素是否在可视页面内
  • 代码如下
const targetNode = document.querySelector('.child');
const intersectionObserver = new IntersectionObserver((entries) => {
   if (entries[0].intersectionRatio <= 0) {
     console.log('视野之外')
   } else {
    console.log('视野之内');
   }
})
intersectionObserver.observe(targetNode);

如何判断元素是否在可视页面内

注册的回调函数将会在主线程中被执行。所以该函数执行速度要尽可能的快。如果有一些耗时的操作需要执行,建议使用 Window.requestIdleCallback() 方法

四、兼容性

  1. 对于第一种方法都是基本API了
  2. 对于第二种也基本兼容了 如何判断元素是否在可视页面内
  3. 对于第三种, 兼容性相对来说就要差一些了 如何判断元素是否在可视页面内
转载自:https://juejin.cn/post/7284164153576538124
评论
请登录