IntersectionObserver
IntersectionObserver接口提供了一种异步观察目标元素与祖先元素或顶级文档viewport的交集中的变化的方法。祖先元素与视窗viewport被称为根(root)。
IntersectionObserver API 是异步的,不随着目标元素的滚动同步触发。
IntersectionObserver的实现,应该采用requestIdleCallback(),即只有线程空闲下来,才会执行观察器。这意味着,这个观察器的优先级非常低,只在其他任务执行完,浏览器有了空闲才会执行。
root 所监听对象的具体祖先元素。如果未传入值或值为null,则默认使用顶级文档的视窗(一般为html)。
rootMargin 计算交叉时添加到根(root)边界盒bounding box的矩形偏移量, 可以有效的缩小或扩大根的判定范围从而满足计算需要。所有的偏移量均可用像素(px)或百分比(%)来表达, 默认值为"0px 0px 0px 0px"。
threshold 一个包含阈值的列表, 按升序排列, 列表中的每个阈值都是监听对象的交叉区域与边界区域的比率。当监听对象的任何阈值被越过时,都会触发callback。默认值为0。
图片/内容懒加载(Lazy Load)
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
// isIntersecting是一个Boolean值,判断目标元素当前是否可见
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src; // 替换自定义属性data-src 为真实 URL
// 图片加载后即停止监听该元素
observer.unobserve(img); // 加载后停止监听
}
});
});
document.querySelectorAll('.lazy-img').forEach(img => observer.observe(img));
埋点曝光
const boxList = [...document.querySelectorAll('.box')]
var io = new IntersectionObserver((entries) =>{
entries.forEach(item => {
// intersectionRatio === 1说明该元素完全暴露出来,符合业务需求
if (item.intersectionRatio === 1) {
// 。。。 埋点曝光代码
io.unobserve(item.target)
}
})
}, {
root: null,
threshold: 1, // 阀值设为1,当只有比例达到1时才触发回调函数
})
// observe遍历监听所有box节点
boxList.forEach(box => io.observe(box))
无限滚动(Infinite Scroll)
监听列表底部元素,触发加载更多数据。
const loader = document.getElementById('load-more');
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
fetchMoreData();
}
});
observer.observe(loader);
自动暂停后台视频
当视频移出视口时暂停播放,节省资源。
const videoObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
entry.target.paused = !entry.isIntersecting;
});
});
scrollIntoView
接口的方法会滚动元素的父容器,主动将元素滚动到视口内的指定位置。
scrollIntoView()
scrollIntoView(alignToTop)
scrollIntoView(scrollIntoViewOptions)
const handleClick=(item)=>{
//item传的对应移动元素的id
const id=document.getElementById(item)
id.scrollIntoView({behavior: "smooth"})
}
alignToTop可选
一个布尔值:
如果为true ,元素的顶端将和其所在滚动区的可视区域的顶端对齐。相应的 。这是这个参数的默认值。
true 对应的是 scrollIntoView Options: {block: "start", inline: "nearest"}
如果为false,元素的底端将和其所在滚动区的可视区域的底端对齐。相应的 。
false 对应的是 scrollIntoView Options: {block: "end", inline: "nearest"}
behavior 可选
定义滚动是立即的还是平滑的动画。该选项是一个字符串,必须采用以下值之一:
smooth:滚动应该是平滑的动画。
instant:滚动应该通过一次跳跃立刻发生。
auto:滚动行为由 scroll-behavior 的计算值决定。
block 可选
定义垂直方向的对齐
inline 可选
定义水平方向的对齐
导航到页面锚点
点击导航菜单时,平滑滚动到对应区块。
document.getElementById('section-2').scrollIntoView({ behavior: 'smooth' });
表单验证错误定位
提交表单时,若校验失败,滚动到第一个错误字段。
const firstError = document.querySelector('.error');
if (firstError) {
firstError.scrollIntoView({ behavior: 'smooth', block: 'center' });
firstError.focus();
}
聊天室最新消息
新消息到达后自动滚动到底部。
const latestMessage = document.getElementById('message-list').lastElementChild;
latestMessage.scrollIntoView({ block: 'end' });
单页应用(SPA)路由切换
切换页面后滚动到顶部。
router.afterEach(() => {
window.scrollTo(0, 0);
// 或使用根元素
// document.documentElement.scrollIntoView();
});
最佳实践
优先使用 IntersectionObserver
在需要监听元素可见性的场景(如懒加载、曝光统计)替代 scroll
事件,减少性能损耗。
// 避免使用 scroll 事件
window.addEventListener('scroll', () => { /* 性能差 */ });
合理配置 IntersectionObserver
通过 threshold
(触发阈值)和 rootMargin
(视口扩展)优化监听精度。
new IntersectionObserver(callback, {
threshold: [0, 0.25, 0.5, 0.75, 1], // 多阈值触发
rootMargin: '100px', // 提前 100px 触发
});
scrollIntoView 的平滑滚动
使用 { behavior: 'smooth' }
提升用户体验
element.scrollIntoView({
behavior: 'smooth',
block: 'start', // 对齐方式(start/center/end/nearest)
inline: 'nearest',
});
性能问题
避免频繁调用 scrollIntoView
,必要时使用防抖或 requestAnimationFrame
。
总结:
IntersectionObserver:用于监听元素可见性变化,适合懒加载、曝光统计等场景。
scrollIntoView:用于主动控制滚动位置,适合导航定位、错误提示等需求。
根据具体场景选择工具,必要时可组合使用以兼顾功能与性能。