- 引用 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. 构造函数
javascript
const observer = new IntersectionObserver(callback, options);
callback
:交叉状态变化时的回调函数,接收两个参数:- entries:IntersectionObserverEntry 对象数组,包含交叉信息;
- observer:当前观察器实例。
- options:配置对象(可选):
2. IntersectionObserverEntry 对象属性

三、核心应用场景与示例代码
1. 图片懒加载
原理:初始加载占位图,元素进入视口时替换为真实 URL。
javascript
// 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. 无限滚动加载
原理:监听底部哨兵元素,触底时加载新数据。
javascript
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. 滚动动画触发
原理:元素进入视口时添加动画类。
javascript
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. 广告曝光统计
原理:记录广告元素的可见时间。
javascript
const adObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
trackAdImpression(entry.target.id); // 发送曝光事件
}
});
}, { threshold: 0.5 }); // 50% 可见时统计
四、React 应用示例
1. 自定义 Hook 封装
jsx
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(支持单次触发)
jsx
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
对象来监听整个页面的滚动事件。
javascript:c:\Users\111\Desktop\前端_工作\手撕代码JS篇\scroll.js
// 方式一:使用 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. 监听元素滚动事件
除了窗口滚动,也能监听特定元素的滚动事件。
html:c:\Users\111\Desktop\前端_工作\手撕代码JS篇\scroll.html
<!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)
节流是指在一定时间内,只执行一次事件处理函数。
javascript:c:\Users\111\Desktop\前端_工作\手撕代码JS篇\scroll.js
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)
防抖是指在事件停止触发一段时间后,才执行事件处理函数。
javascript:c:\Users\111\Desktop\前端_工作\手撕代码JS篇\scroll.js
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. 使用场景
- 懒加载:当滚动到图片所在位置时,再加载图片,减少初始加载时间。
- 吸顶导航:当页面滚动到一定位置时,导航栏固定在页面顶部。
- 无限滚动:当滚动到页面底部时,自动加载更多内容。