IntersectionObserve
这个API,能够晓得的人并不多(我也是近来才晓得…),这个API能够很轻易的监听元素是不是进入了可视地区。
<style>
* {
margin: 0;
padding: 0;
}
.test {
width: 200px;
height: 1000px;
background: orange;
}
.box {
width: 150px;
height: 150px;
margin: 50px;
background: red;
}
</style>
<div class="test">test</div>
<div class="box">box</div>
上图代码中,.box
元素现在并不在可视地区(viewport)内,怎样监听它进入可视地区内?
传统做法是:监听scroll事宜,及时盘算.box
间隔viewport的top值:
const box = document.querySelector('.box');
const viewHeight = document.documentElement.clientHeight;
window.addEventListener('scroll', () => {
const top = box.getBoundingClientRect().top;
if (top < viewHeight) {
console.log('进入可视地区');
}
});
但是scroll
事宜会频仍触发,因而我们还须要手动撙节。
运用IntersectionObserve
就异常轻易了:
const box = document.querySelector('.box');
const intersectionObserver = new IntersectionObserver((entries) => {
entries.forEach((item) => {
if (item.isIntersecting) {
console.log('进入可视地区');
}
})
});
intersectionObserver.observe(box);
IntersectionObserver API是异步的,不跟着目的元素的转动同步触发,所以不会有机能题目。
IntersectionObserver组织函数
const io = new IntersectionObserver((entries) => {
console.log(entries);
});
io.observe(dom);
挪用IntersectionObserver时,须要给它传一个回调函数。当监听的dom元素进入可视地区或许从可视地区脱离时,回调函数就会被挪用。
注重: 第一次挪用new IntersectionObserver
时,callback函数会先挪用一次,纵然元素未进入可视地区。
组织函数的返回值是一个视察器实例。实例的observe要领能够指定视察哪一个 DOM 节点。
// 最先视察
io.observe(document.getElementById('example'));
// 住手视察
io.unobserve(element);
// 封闭视察器
io.disconnect();
IntersectionObserverEntry对象
callback函数被挪用时,会传给它一个数组,这个数组里的每一个对象就是当前进入可视地区或许脱离可视地区的对象(IntersectionObserverEntry对象)
这个对象有许多属性,个中最常常使用的属性是:
- target: 被视察的目的元素,是一个 DOM 节点对象
- isIntersecting: 是不是进入可视地区
- intersectionRatio: 订交地区和目的元素的比例值,进入可视地区,值大于0,不然即是0
options
挪用IntersectionObserver时,除了传一个回调函数,还能够传入一个option对象,设置以下属性:
- 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的值
const io = new IntersectionObserver((entries) => {
console.log(entries);
}, {
threshold: [0, 0.5],
root: document.querySelector('.container'),
rootMargin: "10px 10px 30px 20px",
});
懒加载
图片懒加载的道理就是:给img标签一个自定义属性,用来纪录真正的图片地点。默许img标签只加载一个占位符。当图片进入可视地区时,再把img的src属性替代成真正的图片地点。
<div>
<img src="/empty.jpg" data-src="/img/1.jpg" />
<img src="/empty.jpg" data-src="/img/2.jpg" />
</div>
const intersectionObserver = new IntersectionObserver((entries) => {
entries.forEach((item) => {
if (item.isIntersecting) {
item.target.src = item.target.dataset.src;
// 替代胜利后,住手视察该dom
intersectionObserver.unobserve(item.target);
}
})
}, {
rootMargin: "150px 0px" // 提早150px进入可视地区时就加载图片,进步用户体验
});
const imgs = document.querySelectorAll('[data-src]');
imgs.forEach((item) => {
intersectionObserver.observe(item)
});
办理上报
前端页面常常有上报数据的需求,比方统计页面上的某些元素是不是被用户检察,点击。这时候,我们就能够封装一个Log
组件,用于当元素进入可视地区时,就上报数据。
以React为例,我们愿望:被Log
组件包裹的元素,进入可视地区后,就向背景发送{ appid: 1234, type: 'news'}
数据
<Log
className="container"
log={{ appid: 1234, type: 'news'}}
style={{ marginTop: '1400px' }}
onClick={() => console.log('log')}
>
<div className="news" onClick={() => console.log('news')}>
<p>其他内容</p>
</div>
</Log>
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class Log extends Component {
static propTypes = {
log: PropTypes.object
};
constructor(props) {
super(props);
this.io = null;
this.ref = React.createRef();
}
componentDidMount() {
this.io = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
console.log('进入可视地区,将要发送log数据', this.props.log);
sendLog(this.props.log);
this.io.disconnect();
}
}
);
this.io.observe(this.ref.current);
}
componentWillUnmount() {
this.io && this.io.disconnect();
}
render() {
// 把log属性去掉,不然log属性也会渲染在div上
// <div log="[object Object]"></div>
const { log, ...props } = this.props;
return (
<div ref={this.ref} {...props}>
{this.props.children}
</div>
)
}
}
export default Log
兼容性
safari并不支撑该API,因而为了兼容主流浏览器,我们须要引入polyfill
intersection-observer