IntersectionObserver API 详解与前端实战应用
本文详解 IntersectionObserver API,介绍其构造函数、observe/unobserve/disconnect/takeRecords 方法及回调机制。通过 threshold、root、rootMargin 配置实现精准监听,并展示在图片懒加载、元素吸顶及无限滚动加载更多等场景中的具体代码实现与注意事项。
利用观察者模式来监听并判断 DOM 元素是否位于设备的可视区域内。
本文示例代码基于 Vue 3 框架编写。
1. IntersectionObserver
IntersectionObserver 能够自动检测元素是否进入设备可视区域,无需通过频繁计算坐标来判断可见性。其核心原理是检测目标元素与视口之间是否产生交叉区域,因此该 API 被称为“交叉观察器”。
const observer = new IntersectionObserver(callback, option);
IntersectionObserver 是浏览器原生提供的构造函数,调用时接受两个参数:
- callback:当元素可见性发生变化时触发的回调函数。
- option:可选的配置对象,用于定义监听的详细规则。
该构造函数返回一个观察器实例,该实例包含以下四个主要方法:
- observe:开始监听指定的目标元素。
- unobserve:停止对特定元素的监听。
- disconnect:关闭整个观察器的监听工作。
- takeRecords:返回所有被观察目标的对象数组。
1.1 observe 方法
该方法接收一个 target 参数,其值必须为 Element 类型,用于指定需要被监听的目标 DOM 元素。
// 获取元素
const target = document.getElementById("dom");
// 开始观察
io.observe(target);
1.2 unobserve 方法
该方法接收一个 target 参数,值为 Element 类型,用于指定不再需要监听的目标元素。
// 获取元素
const target = document.getElementById("dom");
// 停止观察
io.unobserve(target);
1.3 disconnect 方法
该方法无需接收参数,调用后可立即关闭观察器的所有监听活动。
// 关闭观察器
io.disconnect();
// 页面加载时监听元素
onMounted(() => {
var demo3 = document.querySelector('document.querySelector(dom)) // 获取元素
var observer = new IntersectionObserver((mutaions)=>{ // 创建IntersectionObserver对象
console.log(mutaions[0].isIntersecting)
})
observer.observe(demo3) // 需要监听的元素
}
1.4 takeRecords 方法
该方法无需接收参数,用于获取所有被观察对象的当前状态,返回值为一个数组。
// 获取被观察元素
const observerList = io.takeRecords();
1.5 callback 参数
当目标元素的可见性发生变化时,观察器的回调函数 callback 会被调用。
callback 通常会被触发两次:一次是目标元素刚刚进入视口,另一次是完全离开视口。
const io = new IntersectionObserver((changes, observer) => {
console.log(changes);
console.log(observer);
});
1.6 options
threshold:决定了何时触发回调函数。它是一个数组,包含多个门槛值(threshold),默认为 [0],即当交叉比例(intersectionRatio)达到 0 时触发。用户可以自定义数组,例如 [0, 0.25, 0.5, 0.75, 1],表示当目标元素可见比例为 0%、25%、50%、75%、100% 时分别触发回调。root:指定用于观察的根元素,默认是浏览器视口。若指定具体元素,则被观察的元素必须是该指定元素的子元素。rootMargin:用于扩大或缩小视口的大小,采用 CSS 定义方式,如 '10px 10px 30px 20px' 分别对应 top、right、bottom 和 left 的值。
2. IntersectionObserverEntry 对象
changes 数组中的每一项都是一个 IntersectionObserverEntry 对象,包含以下属性:
boundingClientRect:目标元素的矩形区域信息。ntersectionRatio:目标元素的可见比例,即 intersectionRect 占 boundingClientRect 的比例。完全可见时为 1,完全不可见时小于等于 0。intersectionRect:目标元素与视口(或根元素)的交叉区域信息。isIntersecting:布尔值,表示目标元素与交集观察者的根节点是否相交(常用属性)。isVisible:布尔值,表示目标元素是否可见(该属性仍处于试验阶段,不建议在生产环境中使用)。rootBounds:根元素的矩形区域信息,即 getBoundingClientRect() 方法的返回值。如果没有根元素(即直接相对于视口滚动),则返回 null。target:被观察的目标元素,是一个 DOM 节点对象(常用属性)。time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒。
3. 是否在可视区域
onMounted(() => {
var observer = new IntersectionObserver((entries) => {
console.log(111111, entries[0].isIntersecting);
dataMap.showMyBox = !entries[0].isIntersecting; //返回true代表在页面可视区域,false代表不在页面可视区域。
});
observer.observe(document.querySelector(dom));
}// 页面卸载时可解绑
onBeforeUnmount(() => {
if (observer) {
observer.unobserve(document.querySelector(dom)); //解绑元素
observer.disconnect(); //停止监听
}
});4. 图片懒加载
使用 IntersectionObserver 实现图片懒加载非常简便。首先需观察懒加载元素,待其进入可视区域后设置图片 src 属性。同时,可结合 IntersectionObserver.rootMargin 实现提前加载,通常设置为 1~2 倍浏览器窗口视口高度,以优化用户体验。
/**
* @method lazyLoad
* @param {NodeList} $imgList 图片元素集合
* @param {number} preloadHeight 预加载高度
*/
export function lazyLoad($imgList, preloadHeight = 1000) {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) { // 目标元素出现在 root 可视区,返回 true
const $target = entry.target
const src = $target.getAttribute('lazyload')
if (src) {
$target.setAttribute('src', src) // 真正加载图片
}
observer.unobserve($target) // 解除观察
}
})
}, {
rootMargin: `0px 0px ${preloadHeight}px 0px`,
})
Array.prototype.forEach.call($imgList, ($item) => {
if ($item.getAttribute('src')) return // 过滤已经加载过的图片
observer.observe($item) // 开始观察
})
}
使用方法如下:
// 图片元素设置 lazyload 属性
<img lazyload="图片链接" alt="图片说明">
// 观察图片元素
lazyLoad(document.querySelectorAll("[lazyload]"))
5. 元素吸顶、吸底
若页面结构较为简单,可直接使用 CSS 粘性布局(sticky positioning)。
使用 IntersectionObserver 实现元素固定(如吸顶)的思路如下:首先给固定元素包裹一层父元素,父元素指定高度以占位,防止固定元素吸附时页面发生抖动。然后观察父元素的可视状态变化,当父元素即将离开可视区域时,修改固定元素的样式。
/**
* @method fixBanner
* @param {HTMLElement} $observeEle 观察元素
* @param {HTMLElement} $fixEle 固定定位元素
*/
export function fixBanner($observeEle, $fixEle) {
const $ele = $fixEle
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
$ele.style.cssText = ''
} else {
$ele.style.cssText = 'position: fixed; top: 0; left: 0'
}
})
}, {
threshold: 1, // threshold 设置为 1 表示目标元素完全可见时触发回调函数
})
observer.observe($observeEle) // 开始观察
}6. 加载更多
使用 IntersectionObserver 实现“加载更多”功能时,需在列表末尾添加一个尾部元素(如加载动画)。当该尾部元素进入可视区域时,触发加载更多数据的逻辑。需注意,尾部元素必须始终位于所有列表元素的后面。
提前加载的高度(rootMargin)不能随意设置,若设置过大,会导致尾部元素始终处于可视状态,从而频繁触发加载请求。
function loadMore() {
const observer = new IntersectionObserver(
(entries) => {
const loadingEntry = entries[0]
if (loadingEntry.isIntersecting) {
// 请求数据并插入列表
}
},
{
rootMargin: '0px 0px 600px 0px', // 提前加载高度
},
)
observer.observe(document.querySelector('.mod_loading')) // 观察尾部元素
}总结
至此,关于 JS 监听元素是否出现在可视区域(IntersectionObserver)的介绍已完成。如需了解更多相关内容,请搜索本站过往文章或继续浏览后续文章,感谢支持!
为什么JS调用reload后页面没有立即更新
2026-11-05 13:38:29
crypto-js 加密结果与 Java/Python 后端不一致怎么解决
2026-11-05 12:09:48
js文件缓存导致代码不更新怎么强制刷新
2026-11-04 22:30:24
JS逆向调试技巧:编写Hook脚本监控加密函数输入输出
2026-11-04 10:42:46
Babylon.js 性能优化技巧:实例化网格与遮挡剔除
2026-11-03 23:43:22
