【前端】如何判断元素在可视区域内?

10/3/2022 JS

# 概述

# 基本概念

可视区域:浏览网页时,设备内肉眼可见的区域。
在日常开发中,经常需要判断目标元素是否在视窗之内或者和视窗的距离小于一个值。

  • 可视区域的运用场景
  • 实现方式
  • offset 系列(只读)
    • offsetTop:元素的上外边框至包含元素的上内边框之间的像素距离
    • offsetLeft:元素的左外边框至包含元素的左内边框之间的像素距离
    • offsetWidth:content + padding(左右) + margin(左右)
    • offsetHeight:content + padding(上下) + margin(上下)
  • client 系列(只读)
    • clientWidth:content + padding(左右)
    • clientHeight:content + padding(上下)
  • scroll 系列(只读)
    • scrollTop:垂直滚动的距离,设为 0 可重置元素滚动位置
    • scrollLeft:水平滚动的距离,设为 0 可重置元素滚动位置
    • scrollWidth:用于确定元素的实际滚动宽度
    • scrollHeight:用于确定元素的实际滚动高度

# offsetTop scrollTop

  • 公式表示:el.offsetTop - document.documentElement.scrollTop <= viewPortHeight

  • 代码实现:

    function isInViewPort(el) {
      const viewPortHeight =
        window.innerHeight ||
        document.documentElement.clientHeight ||
        document.body.clientHeight;
      const offsetTop = el.offsetTop;
      const scrollTop = document.documentElement.scrollTop;
      return offsetTop - scrollTop <= viewPortHeight;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

# getBoundingClientRect

该方法的返回值是一个 DOMRect 对象,拥有 left/top/right/bottom/x/y/width/height 属性。
left/right:视窗的左边界分别到元素的左边框和右边框的距离。
top/bottom:视窗的上边界分别到元素的上边框和下边框的距离。
当页面发生滚动的时候,left/top/right/bottom 属性值都会随之改变。

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

    • top >= 0 且 left >= 0
    • bottom 小于等于视窗高度
    • right 小于等于视窗宽度
  • 代码实现:

    function isInViewPort(el) {
      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

# Intersection Observer

重叠观察者:用于判断两个元素是否重叠,因为不用进行事件的监听,性能方面相比 getBoundingClientRect 会好很多。

  • 创建观察者

    const options = {
      // 表示重叠面积占被观察者的比例(0-1),1 表示完全被包含
      threshold: 1.0,
      // 必须是目标元素的父级元素
      root: document.querySelector('#scrollArea'),
    };
    
    const callback = function (entries, observer) {
      entries.forEach((entry) => {
        entry.time; // 触发的时间
        entry.rootBounds; // 根元素的位置矩形,这种情况下为视窗位置
        entry.boundingClientRect; // 被观察者的位置矩形
        entry.intersectionRect; // 重叠区域的位置矩形
        entry.intersectionRatio; // 重叠区域占被观察者面积的比例(被观察者不是矩形时也按照矩形计算)
        entry.target; // 被观察者
      });
    };
    
    const observer = new IntersectionObserver(callback, options);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  • 传入被观察者

    const target = document.querySelector('.target');
    observer.observe(target);
    
    1
    2

# 图片的懒加载

# 列表的无限滚动

Last Updated: 10/8/2022, 2:29:52 PM