一、用途

可视区域即我们浏览网页的设备肉眼可见的区域,如下图

在日常开发中,我们经常需要判断目标元素是否在视窗之内或者和视窗的距离小于一个值(例如 100 px),从而实现一些常用的功能,例如:

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

二、实现方式

判断一个元素是否在可视区域,我们常用的有三种办法:

  • offsetTop、scrollTop

  • getBoundingClientRect

  • Intersection Observer

offsetTop、scrollTop

offsetTop,元素的上外边框至包含元素的上内边框之间的像素距离,其他offset属性如下图所示:

下面再来了解下clientWidthclientHeight

  • clientWidth:元素内容区宽度加上左右内边距宽度,即clientWidth = content + padding
  • clientHeight:元素内容区高度加上上下内边距高度,即clientHeight = content + padding

这里可以看到client元素都不包括外边距

最后,关于scroll系列的属性如下:

  • scrollWidth 和 scrollHeight 主要用于确定元素内容的实际大小

  • scrollLeft 和 scrollTop 属性既可以确定元素当前滚动的状态,也可以设置元素的滚动位置

    • 垂直滚动 scrollTop > 0
    • 水平滚动 scrollLeft > 0
  • 将元素的 scrollLeft 和 scrollTop 设置为 0,可以重置元素的滚动位置

注意

  • 上述属性都是只读的,每次访问都要重新开始

下面再看看如何实现判断:

公式如下:

el.offsetTop - document.documentElement.scrollTop <= viewPortHeight

代码实现:

function isInViewPortOfOne (el) {
    // viewPortHeight 兼容所有浏览器写法
    const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight 
    const offsetTop = el.offsetTop
    const scrollTop = document.documentElement.scrollTop
    const top = offsetTop - scrollTop
    return top <= viewPortHeight
}

getBoundingClientRect

返回值是一个 DOMRect对象,拥有lefttoprightbottomxywidth, 和 height属性

const target = document.querySelector('.target');
const clientRect = target.getBoundingClientRect();
console.log(clientRect);

// {
//   bottom: 556.21875,
//   height: 393.59375,
//   left: 333,
//   right: 1017,
//   top: 162.625,
//   width: 684
// }

属性对应的关系图如下所示:

当页面发生滚动的时候,topleft属性值都会随之改变

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

  • top 大于等于 0
  • left 大于等于 0
  • bottom 小于等于视窗高度
  • right 小于等于视窗宽度

实现代码如下:

function isInViewPort(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
  );
}

Intersection Observer

Intersection Observer 即重叠观察者,从这个命名就可以看出它用于判断两个元素是否重叠,因为不用进行事件的监听,性能方面相比getBoundingClientRect会好很多

使用步骤主要分为两步:创建观察者和传入被观察者

创建观察者

const options = {
  // 表示重叠面积占被观察者的比例,从 0 - 1 取值,
  // 1 表示完全被包含
  threshold: 1.0, 
  root:document.querySelector('#scrollArea') // 必须是目标元素的父级元素
};

const callback = (entries, observer) => { ....}

const observer = new IntersectionObserver(callback, options);

通过new IntersectionObserver创建了观察者 observer,传入的参数 callback 在重叠比例超过 threshold 时会被执行`

关于callback回调函数常用属性如下:

// 上段代码中被省略的 callback
const callback = function(entries, observer) { 
    entries.forEach(entry => {
        entry.time;               // 触发的时间
        entry.rootBounds;         // 根元素的位置矩形,这种情况下为视窗位置
        entry.boundingClientRect; // 被观察者的位置举行
        entry.intersectionRect;   // 重叠区域的位置矩形
        entry.intersectionRatio;  // 重叠区域占被观察者面积的比例(被观察者不是矩形时也按照矩形计算)
        entry.target;             // 被观察者
    });
};

传入被观察者

通过 observer.observe(target) 这一行代码即可简单的注册被观察者

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

三、案例分析

实现:创建了一个十万个节点的长列表,当节点滚入到视窗中时,背景就会从红色变为黄色

Html结构如下:

<div class="container"></div>

css样式如下:

.container {
    display: flex;
    flex-wrap: wrap;
}
.target {
    margin: 5px;
    width: 20px;
    height: 20px;
    background: red;
}

container插入1000个元素

const $container = $(".container");

// 插入 100000 个 <div class="target"></div>
function createTargets() {
  const htmlString = new Array(100000)
    .fill('<div class="target"></div>')
    .join("");
  $container.html(htmlString);
}

这里,首先使用getBoundingClientRect方法进行判断元素是否在可视区域

function isInViewPort(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;
}

然后开始监听scroll事件,判断页面上哪些元素在可视区域中,如果在可视区域中则将背景颜色设置为yellow

$(window).on("scroll", () => {
    console.log("scroll !");
    $targets.each((index, element) => {
        if (isInViewPort(element)) {
            $(element).css("background-color", "yellow");
        }
    });
});

通过上述方式,可以看到可视区域颜色会变成黄色了,但是可以明显看到有卡顿的现象,原因在于我们绑定了scroll事件,scroll事件伴随了大量的计算,会造成资源方面的浪费

下面通过Intersection Observer的形式同样实现相同的功能

首先创建一个观察者

const observer = new IntersectionObserver(getYellow, { threshold: 1.0 });

getYellow回调函数实现对背景颜色改变,如下:

function getYellow(entries, observer) {
    entries.forEach(entry => {
        $(entry.target).css("background-color", "yellow");
    });
}

最后传入观察者,即.target元素

$targets.each((index, element) => {
    observer.observe(element);
});

可以看到功能同样完成,并且页面不会出现卡顿的情况

JavaScript如何判断一个元素是否在可视区域中?的更多相关文章

  1. JS代码片段:判断一个元素是否进入可视区域

    // Determine if an element is in the visible viewport function isInViewport(element) { var rect = el ...

  2. javascript判断某种元素是否进入可视区域

    判断是否在指定的可视区域内,先用最简单的方式,比如整个页面为可视区域 找到几个关键因素: sTop= $(window).scrollTop();  //滚动条距顶部的高度 clientHeight= ...

  3. iOS 如何判断一个点在某个指定区域中

    在iOS 开发中会遇到 判断位置的情况 iOS 自己都有函数实现的这些功能. 判断一个点是否在这个rect区域中 bool CGRectContainsPoint(CGRect rect,CGPoin ...

  4. Python3基础 not in列表名 判断一个元素是否不在列表中列表中

    镇场诗:---大梦谁觉,水月中建博客.百千磨难,才知世事无常.---今持佛语,技术无量愿学.愿尽所学,铸一良心博客.------------------------------------------ ...

  5. 如何判断一个Div是否在可视区域,判断div是否可见

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. js 判断一个元素是否在滚动的可视区域内,不在就固定到可视区域的上方。

    前言:最近工作中,有这样一个场景,判断一个元素是否在滚动的可视区域内,不在就固定到可视区域的上方.为了以后再次遇到,所以记录下来,并分享.转载请注明出处:https://www.cnblogs.com ...

  7. javascript判断一个元素是另外一个元素的子元素

    javascript判断一个元素是另外一个元素的子元素用途有很多,最常用的就是当点击页面的空白处去执行某些操作,比如弹出层等. function isParent (obj,parentObj){ w ...

  8. 如何判断元素是否在可视区域ViewPort

    个性签名: 生如夏花,逝如冬雪:人生如此,何悔何怨. 前言: 经常需要计算元素的大小或者所在页面的位置,offsetWidth,clientWidth,scrollWidth,scrollTop这几个 ...

  9. jquery and js 判断一个元素是否存在

    一.javascript中判断一个元素是否存在 if(document.getElementById('example')){ // do sth } 二.jquery中判断一个元素是否存在 < ...

  10. jQuery判断一个元素是否为另一个元素的子元素(或者其本身)

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head& ...

随机推荐

  1. IDEA 报错 project is already registered

    1.环境&背景 有时候IEDA下一个工程中想引入多个项目,只能添加多个modules. 版本:IDEA 2020.2 构建:gradle 2.问题 有时候删掉了某个项目再导入时报错 proje ...

  2. live [lɪv , laɪv] 动词读lɪv 形容词读laɪv

    live [lɪv , laɪv] 动词读lɪv 形容词读laɪv live 英 [lɪv , laɪv] 美 [lɪv , laɪv] v. 居住;住;生存;(尤指在某时期)活着;(以某种方式)生活 ...

  3. thinkphp phpstorm xdebug 环境配置

    php5.6 环境配置 phpStudy 开启 Apache 网站 的php版本选择7的 (7的可能自己需要装一下) 获取xdebug前的 检查准备 打开 http://localhost:8033/ ...

  4. Python下使用串口发送十六进制数据

    import serial from time import sleep def recv(serial): while True: data = serial.read_all() if data ...

  5. 基于六轴传感器MPU6050的加速度和角度值读取

    一 系统简介 1.简介 MPU-60x0 是全球首例 9 轴运动处理传感器.它集成了 3 轴MEMS陀螺仪,3 轴MEMS加速度计,以及一个可扩展的数字运动处理器 DMP(Digital Motion ...

  6. Shell脚本自动下载FTP文件上传到S3

    1. shell脚本下载 #!/bin/bash #用户名 USER=xxx #密码 PASSWORD=xxx #下载文件临时目录 SRCTDIR=/approveform/uat/tempin #S ...

  7. 安装libevent

    1.在libevent官网(http://libevent.org/)上下载压缩包(我下载的是libevent-2.1.8-stable.tar.gz) 2.解压压缩包:tar -zxvf libev ...

  8. 3DCAT云流送技术如何搅动各大行业

    在不久前结束的数字会展上,3DCAT展位的实时云流送技术技惊四座,带来不一样的视觉体验,到访的客户都无法分辨这些数字内容是在本地还是云端运行的,每一个粒子都真实可见,有现场的参观者瞪大了双眼,直呼不可 ...

  9. 自定义Key类型的字典无法序列化的N种解决方案

    当我们使用System.Text.Json.JsonSerializer对一个字典对象进行序列化的时候,默认情况下字典的Key不能是一个自定义的类型,本文介绍几种解决方案. 一.问题重现 二.自定义J ...

  10. 恶意软件开发(三)经典DLL注入流程

    什么是dll注入? DLL注入允许将外部DLL文件加载到进程中并运行其中的代码.DLL(动态链接库)是一种可重用的代码库,它包含在多个程序中使用的函数.类.变量和其他程序代码.DLL注入技术可以通过将 ...