介绍

IntersectionObserver 的作用是监听某个元素是否出现在框内 (比如 viewport).

它可以实现 lazy load image, 一开始图片是没有加载的, 当图片出现在 viewport 时才去加载.

也可以用来做 tracking, 比如某个商品 (card 元素), 是否出现在 viewport, 这样就可以检测用户是否看了某个商品.

效果

参考:

IntersectionObserver’s Coming into View

Medium – IntersectionObserver’s coming into view

Trust is good, observation is better: Intersection Observer v2 (v2, 但是目前只有 chrome 99 支持, 所以这篇先不介绍了)

getBoundingClientRect

参考: getBoundingClientRect() 详解

先了解一下 rect, 通过 getBoundingClientRect 可以得知一个元素, 对标 viewport 的位置坐标. 它不管 scroll bar 有点像 position fixed 对标 viewport.

x, y, width, height 是 detect 到的,其它的都是冗余:

top 就是 y

left 就是 x

right 就是 left + width

bottom 就是 top + height

另外,width height 会受到 scale 的影响哦。

在 IntersectionObserver 诞生以前, 要模拟它的效果就得监听 scroll 然后通过调用 getBoundingClientRect 来判断 2 个元素是否交会.

场景

先搭建一个简单的场景. 方便解释

<div class="container">
<div class="box">box1</div>
<div class="box">box2</div>
<div class="box">box3</div>
<div class="box">box4</div>
<div class="box">box5</div>
<div class="box">box6</div>
<div class="box">box7</div>
<div class="box">box8</div>
<div class="box">box9</div>
<div class="box">box10</div>
</div>

1 个 container 包着 10 个 box

CSS Style

.container {
margin-top: 100px;
margin-inline: auto;
width: fit-content;
.box {
border: 1px solid red;
width: 100px;
height: 100px;
}
}

效果

new IntersectionObserver()

const io = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
console.log(entry);
});
},
{
root: document,
rootMargin: "0px",
threshold: [0],
}
);

首先看看它的调用方式.

new 一个 IntersectionObserver 实例.

第 1 个参数是触发时的回调. 不关心这个先.

第 2 个参数是一个 config.

root 指的是框, 一旦观察元素出现在框内就会触发回调. 默认是 viewport 也就是 document, 也可以设置成任何一个 element (通常是 scrollable 的).

rootMargin 指的是 extra 的范围, 如同框变大了. 比如说, 不想等到 element 出现在框内了才触发, 想提早, 那可以写 rootMargin: 50% (也可以写 px, percentage 对应框的大小)

所以它会提早半个屏幕就触发. 一般上 lazy load 图片都会提早, 而不是等到用户已经看见 img element 了才去 load, 这样就慢掉了.

threshold 是一个元素显示多少 % 时要触发, 比如

threshold: [0.1, 0.5, 1], 观察元素是 box4

分别会在这 3 个阶段触发回调. 比较常用的方式是 [0, 1], 刚出现时触发一次, 完整出现时触发一次.

注意:

1. 如果被观察的元素 height 超过框的 height, 那意味着永远不会出现 100% 显示. 那么 1 就不出触发了.

2. 只要元素出现在框内就会触发 (哪怕 viewport 看不见), 看下面的例子:

root 是 container 框 (不是 viewport 哦), click button 会 scroll container. 当把 viewport 移开以后, 点击 button 依然触发了. 因为元素显示在 container 框了.

它不需要出现在 viewport 的框.

3. 不仅仅是 scroll 无论以何种方式出现在框内都会触发, 比如 transform translate 也会触发的.

observe, unobserve

observe

把框定义好以后, 就开始放入要观察的元素.

const box9 = document.querySelectorAll(".box")[8];
io.observe(box9);

注意:

1. 触发频率, 它不是立马触发的 (性能考量), 可能会有几毫秒的微差, 比如监听的是 0.5 (50% 显示), 但触发的时候是 0.52 (intersectionRatio).

2. observe 调用后, 不管元素是否出现在框内, 它都会触发第一次.

unobserve, disconnect

io.unobserve(box9)
io.disconnect()

不想监听了,可以调用 unobserve 把指定的元素监听去掉。

disconnect 并不会把 IntersectionObserver 整个关掉,它只是会把所以当前监听的元素 unobserve 而已。

没有 re-connect 的功能,我们也无法获取当前监听了的元素。

Callback Info

const io = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
console.log(entry);
// entry.target;
// entry.boundingClientRect;
// entry.intersectionRect;
// entry.isIntersecting;
// entry.time;
// entry.intersectionRatio;
// entry.rootBounds
});
}
);

当元素显示时, 它就会触发 callback, entries 就是这一轮所有触发的 element (不是所有监听的 element 哦, 不关事的不会在 entries 里)

出于性能原因, 即使 element 是先后出现在框的, 但也可能被放到同一轮触发列表内.

target = 被观察的元素.

boundingClientRect = 被观察元素的 getBoundingClientRect(). (记得哦, getBoundingClientRect 一定是对标 viewport 的坐标, 所以这里是 target element 对标 viewport)

intersectionRatio = 多少 percentage 显示, 0.52 = 52% 显示在框内

isIntersecting = 元素是否显示

当 ratio = 0 的时候, 有可能是刚出现, 也可能是刚离开. 通过 isIntersecting 来判断.

rootBounds = 框 element 的 getBoundingClientRect(), (new IO 时的 root element 对标 viewport 坐标)

intersectionRect 

它和 boundingClientRect 差不多, 微差体现在:

区别在于它的 height 和 bottom, bottom 是冗余, 我们看 height 就好了.

100px 是整个 box9 的高, intersectionRect 是 41px, 因为 threshold 是 0.4, 40% 显示时触发.

所以 intersection 的 height 拿的不是完整的 box9 height, 而是已经显示的 box9 height. 所以就是大约 40px 了.

height 不同, bottom 自然也就不同了. 因为 bottom = y + height.

time = IO 有一个计时器, 从 new IO 后开始跑 start from 0ms.

每当触发的时候它就去截取当前的 time, 比如 5000 表示从 new IO 到这个元素触发已经过了 5 秒了. 它会一直跑, 不会 reset 的.

通过时间差就可以判断用户是否停留看着某个元素多久了. 具体的需求可能是, 想 tracking 某个 page section 用户是否游览. 但是那种一秒涮过的不算.

这时候就可以看 intersect in/out 的时间差, 来判断是否用户是慢慢划过的.

触发时机

处于性能考量,IntersectionObserver 触发的非常晚

window.setTimeout(() => {
const h1 = document.querySelector('h1')!;
h1.classList.add('showing'); const io = new IntersectionObserver(() => {
console.log('IntersectionObserver', performance.now()); // 3, 1106.3999999910593
h1.classList.add('showed');
});
io.observe(h1); requestAnimationFrame(() => {
console.log('first requestAnimationFrame', performance.now()); // 1, 1086.5999999940395
requestAnimationFrame(() => {
console.log('second requestAnimationFrame', performance.now()); // 2, 1105.0999999940395
});
});
}, 1000);

第一次 requestAnimationFrame 触发在 ui render 之前

第二次 requestAnimationFrame 触发在 ui render 之后

IntersectionObserver 比第二次 requestAnimationFrame 还要晚触发。

DOM – IntersectionObserver的更多相关文章

  1. IntersectionObserver API

    温馨提示:本文目前仅适用于在 Chrome 51 及以上中浏览. 2016.11.1 追加,Firefox 52 也已经实现. 2016.11.29 追加,Firefox 的人担心目前规范不够稳定,未 ...

  2. 使用IntersectionObserver更高效的监视某个页面元素是否进入了可见窗口

    比如说,你想跟踪 DOM 树里的一个元素,当它进入可见窗口时得到通知. 也许想实现即时延迟加载图片功能,或者你需要知道用户是否真的在看一个广告 banner. 你可以通过绑定 scroll 事件或者用 ...

  3. IntersectionObserver API 使用教程

    转载:原文地址:http://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html 网页开发时,常常需要了解某个元素是否进入了&q ...

  4. IntersectionObserver简介

    写在前面 在移动端,有个很重要的概念,叫做懒加载,适用于一些图片资源特别多,ajax数据特别多的页面中,经常会有动态加载数据的场景中,这个时候,我们通常是使用监听scroll或者使用setInterv ...

  5. IntersectionObserver

    创建对象 var io = new IntersectionObserver(callback, option); IntersectionObserver是浏览器原生提供的构造函数,接受两个参数:c ...

  6. IntersectionObserver API,观察元素是否进入了可视区域

    网页开发时,常常需要了解某个元素是否进入了"视口"(viewport),即用户能不能看到它. 上图的绿色方块不断滚动,顶部会提示它的可见性. 传统的实现方法是,监听到scroll事 ...

  7. 谈谈IntersectionObserver懒加载

    概念 IntersectionObserver接口(从属于Intersection Observer API)为开发者提供了一种可以异步监听目标元素与其祖先或视窗(viewport)交叉状态的手段.祖 ...

  8. js IntersectionObserver api

    API const options = { root: null, threshold: [0, 0.5, 1], rootMargin: '30px 100px 20px' } var io = n ...

  9. IntersectionObserver API 使用教程(转载)

    作者: 阮一峰 日期: 2016年11月 3日 网页开发时,常常需要了解某个元素是否进入了"视口"(viewport),即用户能不能看到它. 上图的绿色方块不断滚动,顶部会提示它的 ...

  10. 使用IntersectionObserver制作滚动动画以及其他记录

    前言 最近在重做公司项目的主页,正好新来了个UI,整个都重新设计了一下,动画还挺多的.我之前没有怎么玩过这些,踩了挺多坑,最后找到了目前而言最合适的方法,现在做一个记录. 需要把原来的主页从项目中抽出 ...

随机推荐

  1. oeasy教您玩转vim - 65 - # 批处理操作

    ​ 批处理操作 回忆上次 我们上次参数列表 arguments list 所谓参数列表指的是 vim 打开的 参数列表 参数会加载到内存中成为 buffer 参数的控制 :arga filename ...

  2. TIER 1: Appointment

    TIER 1: Appointment SQL Structured Query Language 是一种用于管理关系型数据库的编程语言.它是一种标准化的语言,用于定义.操作和管理数据库中的数据. 经 ...

  3. 毕设项目:springboot+vue实现的在线求职平台

    一.前言 随着信息技术的飞速发展和互联网的普及,线上求职已成为众多求职者和企业招聘的重要渠道.为满足市场需求,我们利用Spring Boot和Vue技术栈,开发了一款功能全面.用户友好的在线求职平台. ...

  4. k8s(3) 集群运行

    Master下面执行 mkdir -p $HOME/.kube 执行的脚本,需要读取的配置文件 cp -i /etc/kubernetes/admin.conf $HOME/.kube/config ...

  5. 老旧 Linux 系统搭建现代 C++ 开发环境 —— 基于 neovim

    问题背景 公司配发的电脑是 macOS,日常开发需要访问 Linux 虚拟机,出于安全方面的考虑,只能通过跳板机登录.这阻止了大多数远程图形界面的使用,让写代码的工作变得复杂起来,市面上非常好用的 V ...

  6. 第一讲:日志系统:一条sql更新语句是如何执行的?

    目录 第一讲:日志系统:一条sql更新语句是如何执行的? 回顾 抛出问题: 提出思路: 重要的日志模块:redo log 重要的日志模块:binlog 为什么会有两份日志呢? 这两种日志有以下三点不同 ...

  7. 【WSDL】03 使用注解自定义服务信息

    对原来的自定义WebService设置注解: package cn.cloud9.jax_ws.server.intf; import javax.jws.WebMethod; import java ...

  8. 【Redis】02 Redis 搭建与操作

    Redis的安装及启动停止 官网地址: https://redis.io/download 使用wget命令下载redis wget 下载地址 下载: [root@VM-0-7-centos ~]# ...

  9. rllab —— 强化学习 算法模块库 (环境配置失败!!!) (已经不进行维护更新,已被淘汰,由于一些老旧代码还有可能关联因此fork了下,复制了份原始项目)

    代码地址: https://gitee.com/devilmaycry812839668/rllab 由于官方代码地址在github上,外网偶尔会出现网络连接中断的问题因此在国内代码库中fork了一个 ...

  10. uniapp tabBar配置后不显示

    pages 和tabbar 箭头所指位置 第一个必须一样 写法也要完全一致 上面这样写 下面这样写 这样都不能匹配上正常显示