IntersectionObserver API应用场景示例代码详解
- 引用 13 篇资料作为参考
- 深入理解 JavaScript 的 IntersectionObserver API-CSDN博客
- javascript - IntersectionObserver API 手把手教学 - 个人文章 - SegmentFault 思否
- 前端必备‘神器’:解锁JS IntersectionObserver的超强魔力_new intersectionobserver-CSDN博客
- 深入浅出详解Intersection Observer交叉观察器 API
- 使用 IntersectionObserver 接口监听目标元素是否进入视口(viewport)_new intersectionobserver 监听dom元素是否在试图范围-CSDN博客
- 你有使用过intersectionObserver吗?说说你对它的理解 - 王铁柱6 - 博客园
- IntersectionObserver实战指南:应用场景与封装实现_intersectionobserver 选项-CSDN博客
- 将 Intersection Observer 与自定义 React Hook 结合使用_useintersectionobserver reacthook-CSDN博客
- useIntersectionObserver - CSDN文库
- 在react当中利用IntersectionObserve实现下拉加载数据_react intersection observer-CSDN博客
- 面试判断元素是否在可视区域中IntersectionObserver详解_javascript技巧_脚本之家
- 面试判断元素是否在可视区域中IntersectionObserver详解-编程学习网
- 面试官:MutationObserver与IntersectionObserver 傻傻分不清楚?
好的,面试官。下面我将详细讲解 JavaScript 中的 IntersectionObserver API,涵盖其核心概念、参数、应用场景及 React 集成示例。
一、IntersectionObserver 核心概念与优势
IntersectionObserver 是一个异步监听
目标元素与视口(或指定祖先元素)交叉状态的浏览器原生 API。它解决了传统滚动检测(如监听 scroll 事件 + getBoundingClientRect
)的性能问题:
- 传统方式缺陷:
同步计算布局
,频繁触发重排/重绘,导致卡顿;需手动节流/防抖
,逻辑复杂且易出错。
- IntersectionObserver 优势:
异步回调
:仅在交叉状态变化时触发
,避免主线程阻塞
;- 高效精准:浏览器底层优化,
性能提升显著
(Google 案例实测提升 300%); 配置灵活
:支持阈值、根元素、边距
等定制。
二、参数详解与配置
1. 构造函数
const observer = new IntersectionObserver(callback, options);
callback
:交叉状态变化时的回调函数,接收两个参数:- entries:IntersectionObserverEntry 对象数组,包含交叉信息;
- observer:当前观察器实例。
- options:配置对象(可选):
2. IntersectionObserverEntry 对象属性
三、核心应用场景与示例代码
1. 图片懒加载
原理:初始加载占位图,元素进入视口时替换为真实 URL。
// 1. HTML: <img data-src="real.jpg" src="placeholder.jpg">
const lazyLoad = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; // 加载真实图片 observer.unobserve(img); // 停止观察 } });
}, { rootMargin: "200px", // 提前 200px 加载 threshold: 0.01 // 至少 1% 可见时触发
}); document.querySelectorAll("img[data-src]").forEach(img => { lazyLoad.observe(img);
});
优化点:
- rootMargin 预加载视野外图片;
- 加载后 unobserve 减少无效监听。
2. 无限滚动加载
原理:监听底部哨兵元素,触底时加载新数据。
const sentinel = document.querySelector("#scroll-sentinel");
const loader = new IntersectionObserver((entries) => { if (entries[0].isIntersecting) { loadMoreData(); // 加载数据 // 重置哨兵元素观察(新内容追加后) loader.observe(document.querySelector("#new-sentinel")); }
}, { threshold: 1 }); // 完全进入视口时触发 loader.observe(sentinel);
3. 滚动动画触发
原理:元素进入视口时添加动画类。
const animator = new IntersectionObserver((entries) => { entries.forEach(entry => { entry.target.classList.toggle("animate", entry.isIntersecting); });
}, { threshold: 0.3 }); // 30% 可见时触发 document.querySelectorAll(".fade-in").forEach(el => { animator.observe(el);
});
4. 广告曝光统计
原理:记录广告元素的可见时间。
const adObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { trackAdImpression(entry.target.id); // 发送曝光事件 } });
}, { threshold: 0.5 }); // 50% 可见时统计
四、React 应用示例
1. 自定义 Hook 封装
import { useState, useEffect, useRef } from "react"; function useInView(options = { threshold: 0.1 }) { const [inView, setInView] = useState(false); const ref = useRef(null); useEffect(() => { const observer = new IntersectionObserver(([entry]) => { setInView(entry.isIntersecting); }, options); if (ref.current) observer.observe(ref.current); return () => { if (ref.current) observer.unobserve(ref.current); }; }, [options]); return [ref, inView];
} // 组件中使用
function MyComponent() { const [targetRef, isInView] = useInView(); return ( <div ref={targetRef}> {isInView ? "元素可见" : "元素不可见"} </div> );
}
2. 增强版 Hook(支持单次触发)
function useLazyLoad(options = { once: true }) { const [isLoaded, setIsLoaded] = useState(false); const ref = useRef(null); useEffect(() => { const observer = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) { setIsLoaded(true); if (options.once) observer.unobserve(entry.target); } }, { threshold: 0.01 }); if (ref.current) observer.observe(ref.current); return () => observer.disconnect(); }, []); return [ref, isLoaded];
} // 图片懒加载组件
function LazyImage({ src, alt }) { const [ref, loaded] = useLazyLoad(); return <img ref={ref} src={loaded ? src : "placeholder.jpg"} alt={alt} />;
}
五、面试要点总结
- 核心价值:
解决传统滚动监听性能瓶颈,实现高效元素可见性检测
。 - 关键配置:
- threshold
控制触发灵敏度
; - rootMargin
扩展/收缩检测边界
。
- threshold
- 应用场景:
懒加载、无限滚动、动画触发、广告曝光
。 - React 最佳实践:
- 使用
useRef 绑定 DOM 元素
; - 在
useEffect 中初始化和清理观察器
。
- 使用
- 优化策略:加载后及时 unobserve,避免内存泄漏。
⚠️ 注意兼容性:
- 支持 Chrome 51+、Firefox 55+、Safari 12.1+;
- 旧版浏览器需引入 intersection-observer Polyfill。
通过 IntersectionObserver,开发者能以声明式优化性能密集型交互(如长列表渲染)
,显著提升用户体验,是现代前端开发的必备技能。
向面试官介绍 IntersectionObserver
在面试中,您可以这样介绍:
IntersectionObserver 是浏览器提供的原生 API,(异步)用于高效地监听元素与视口的交叉状态变化。相比传统的监听 scroll 事件并计算元素位置的方法,IntersectionObserver 提供了更好的性能和更简洁的代码结构。它广泛应用于图片懒加载、无限滚动、元素曝光统计和动画触发等场景。在 React 项目中,我们可以封装自定义 Hook,如
useInView
,以更方便地检测元素的可见性,从而提升用户体验和页面性能。
Scrooll监听的不足
在 JavaScript 里,scroll
事件会在元素的滚动位置发生变化时触发,常见于窗口滚动或者有滚动条的元素滚动时。下面从监听方式、优化方法和使用场景等方面详细介绍。
1. 监听窗口滚动事件
可以通过 window
对象来监听整个页面的滚动事件。
// 方式一:使用 addEventListener
window.addEventListener('scroll', function() {console.log('窗口滚动了');// 获取滚动的垂直距离const scrollTop = window.pageYOffset || document.documentElement.scrollTop;console.log(`当前滚动的垂直距离: ${scrollTop}px`);
});// 方式二:使用 onscroll 属性
window.onscroll = function() {console.log('窗口又滚动了');
};
代码解释
addEventListener
:这是推荐的监听事件方式,它支持为同一个事件添加多个监听器,且能控制事件的冒泡和捕获阶段。onscroll
:这是传统的事件绑定方式,一个元素只能绑定一个onscroll
事件处理函数,如果多次赋值会覆盖之前的处理函数。window.pageYOffset
与document.documentElement.scrollTop
:window.pageYOffset
是现代浏览器获取窗口垂直滚动距离的属性
,document.documentElement.scrollTop
是兼容旧版 IE 浏览器的方式。
2. 监听元素滚动事件
除了窗口滚动,也能监听特定元素的滚动事件。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><style>#scrollable {width: 300px;height: 200px;overflow: auto;border: 1px solid #ccc;}#content {height: 500px;}</style>
</head>
<body><div id="scrollable"><div id="content"></div></div><script>const scrollableElement = document.getElementById('scrollable');scrollableElement.addEventListener('scroll', function() {console.log('元素滚动了');// 获取元素滚动的垂直距离const scrollTop = this.scrollTop;console.log(`元素滚动的垂直距离: ${scrollTop}px`);});</script>
</body>
</html>
3. 性能优化
频繁触发 scroll
事件会带来性能问题,常见的优化方法有节流和防抖。
节流(Throttle)
节流是指在一定时间内,只执行一次事件处理函数。
function throttle(func, delay) {let timer = null;return function() {if (!timer) {func.apply(this, arguments);timer = setTimeout(() => {timer = null;}, delay);}};
}window.addEventListener('scroll', throttle(function() {console.log('节流后的滚动事件');
}, 200));
防抖(Debounce)
防抖是指在事件停止触发一段时间后,才执行事件处理函数。
function debounce(func, delay) {let timer = null;return function() {clearTimeout(timer);timer = setTimeout(() => {func.apply(this, arguments);}, delay);};
}window.addEventListener('scroll', debounce(function() {console.log('防抖后的滚动事件');
}, 300));
4. 使用场景
- 懒加载:当滚动到图片所在位置时,再加载图片,减少初始加载时间。
- 吸顶导航:当页面滚动到一定位置时,导航栏固定在页面顶部。
- 无限滚动:当滚动到页面底部时,自动加载更多内容。