引言
最近在浏览器mdn时发现一个很牛逼的api,该方法主要用于监听页面元素出现在视口时的回调。我们一探究竟吧。
一:初探
1. 方法介绍
-
创建IntersectionObserver对象,内部传递一个回调函数,当监听的dom出现或消失在根(页面)视野时都会触发一次回调。
const observer = new IntersectionObserver(targets=>{ //targets是一个数组,保存被监听的dom对象 })
-
监听页面指定的元素,如果想要监听多个元素,继续添加observe方法就可以。
xxx.observe(dom1) xxx.observe(dom2) xxx.observe(dom3)
-
停止监听指定dom对象
xxx.unobserve(dom)
-
释放监听,全局停止
xxx.disconnect()
2. 例子演示
html部分
<div class="target">
css部分
body{
height: 2000px;
padding-top: 1000px;
}
.target {
width: 100%;
height: 100px;
background-color: red;
}
js部分
let oBox = document.querySelector('.target');
const observer = new IntersectionObserver(entries => {
//也可以使用entries[0].isIntersecting判断是否出现视野
if(entries[0].intersectionRatio>0) {
console.log('出现视野');
}else {
console.log('消失视野')
}
})
observer.observe(oBox);
效果演示
可以看到当盒子出现在视口与消失视口时都会触发回调函数。
二:模拟图片懒加载(原生js版+react hooks版)
js版本
html
<div class="target">
<div class="item">
<img data-src="./1.jpg" alt="">
</div>
<div class="item">
<img data-src="./2.jpg" alt="">
</div>
<div class="item">
<img data-src="./1.jpg" alt="">
</div>
<div class="item">
<img data-src="./2.jpg" alt="">
</div>
<div class="item">
<img data-src="./1.jpg" alt="">
</div>
<div class="item">
<img data-src="./2.jpg" alt="">
</div>
</div>
css
body{
height: 2000px;
}
.item{
height: 250px;
margin-bottom: 20px;
border: 1px solid red;
}
.item img{
width: 400px;
height: 250px;
}
js
let oBox = document.querySelectorAll('img');
const observer = new IntersectionObserver(entries => {
entries.forEach(item=>{
if(item.intersectionRatio>0) {
//1.获取图片data-src的值
const src = item.target.getAttribute('data-src');
//2.绑定图片的src上
item.target.setAttribute('src',src);
//3.停止监听当前图片对象
observer.unobserve(item.target);//停止监听
}
})
})
// 添加对某个元素的监听
for(let i=0;i<oBox.length;i++) observer.observe(oBox[i]);
效果
观察左下角的dom节点变化,当页面滚动到当前图片视口时,图片自动添加src属性。
react版本
ImgLazy组件
import React,{useEffect,useRef} from "react";
const ImgLazy = (props:{src:string,alt?:string}) => {
const {src,alt} = props;
const imgRef = useRef<HTMLImageElement>(null);
useEffect(()=>{
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if(entry.isIntersecting) {
if(imgRef.current) imgRef.current.src = src;
observer.disconnect()
}
})
})
// 添加对某个元素的监听
imgRef.current && observer.observe(imgRef?.current);
return ()=>{
observer.disconnect()
}
},[])
return < img alt={alt} ref={imgRef}/>
}
export default ImgLazy;
测试使用
import React from 'react';
import LazyLoad from "./useLazy";
import Img1 from "./imgs/1.jpg";
import Img2 from "./imgs/3.png";
import './App.css';
function App() {
const imgList = [
{src:Img1,alt:'img1'},
{src:Img2,alt:'img2'},
{src:Img1,alt:'img3'},
{src:Img2,alt:'img4'},
{src:Img1,alt:'img5'},
{src:Img2,alt:'img6'},
]
return (
<div className='box'>
{
imgList && imgList.map(item=>(
<div key={Date.now()} className='item'>
<LazyLoad alt={item.alt} src={item.src}/>
</div>
))
}
</div>
)
}
export default App;
App.css
.item{
height: 300px;
margin-bottom: 20px;
border: 1px solid red;
}
.item img{
width: 500px;
height: 250px;
}
页面效果
三:模拟触底加载
js版本
设计思路:
触底加载的思路十分明确,当浏览器滚动到列表底部时,发起新的数据请求完成数据的拼接。 那么我们基于IntersectionObserver如何实现?我们只需在列表的底部添加一个兜底盒子,当兜底盒子出现在页面视口时就触发数据请求,完成数据拼接。
html部分
<div class="target">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="bottom"></div>
</div>
css部分
body{
height: 2000px;
}
.target {
width: 100%;
}
.item{
width: 100%;
height: 250px;
border: 1px solid red;
margin-bottom: 20px;
}
.bottom{
width: 100%;
height: 10px;
background: gold;
}
js部分
//1.获取兜底盒子,添加监听
let oTarget = document.querySelector('.bottom')
let oBox = document.querySelector('.target');
const observer = new IntersectionObserver(entries => {
console.log(entries)
if(entries[0].intersectionRatio>0) {
//2.兜底盒子出现视野范围内,继续加载数据,节点动态插入到兜底盒子前面
for(let i=0;i<5;i++) {
let oItem = document.createElement('div');
oItem.className = 'item';
oBox.insertBefore(oItem,oTarget)
}
}
})
observer.observe(oTarget);
效果
总结
看董这两个案例,不禁感叹IntersectionObserver真踏马好用啊,谢谢各位爷支持。