IntersectionObserver
定义
就是监听根元素与目标元素在指定的交叉比例时触发回调
兼容性
- 兼容部分现代游览器(chrome)生产环境中使用,请注意兼容需求
使用场景
- 在一定使用场景可替代onscroll
1、写法上更加简单优雅
2、避免了onscroll带来的大量位置计算,提高性能 - 例如1:滚动(上拉)加载
- 例如2: 懒加载
API介绍
创建一个实例
var observer = new IntersectionObserver(callback,options);
参数
callback:指定比例交叉时触发的回调
new IntersectionObserver((entries) => {
},options)
- entries 描述交叉状态,具体可见 entries
options:配置参数
字段 | 类型 | 默认值 | 描述 |
---|---|---|---|
root | element | null(默认为浏览器视窗) | 根元素 |
rootMargin | string | 0 | root外边距,类似margin可为负数('10px -10px 10px -10px') |
threshold | number/ary | 0 | 触发回调的交叉比例 |
方法
- IntersectionObserverEntry.boundingClientRect 使IntersectionObserver对象停止监听工作
- IntersectionObserver.observe() 对象的observe() 方法向, 但是可以监视多个目标元素,以查看这些目标元素可见区域的变化。
- IntersectionObserver.takeRecords() 返回所有观察目标的IntersectionObserverEntry对象数组
- IntersectionObserver.unobserve()使IntersectionObserver停止监听特定目标元素。
加载更多实现(原生)
index.html
- 设置一个固定元素作为哨兵
...LoadingMore....
index.js
class LoadMore {
constructor(options) {
this.observer = null
this.rootNode = document.getElementById(options.root) // 父级容器
this.obsNode = document.getElementById(options.obs) // 守卫节点
this.loadMoreFn = options.loadMoreFn // 加载回调
this.pageNum = 0 // 记录页数
this.total = -1 // 总页数 -1 // 尚未获取
this.init() // 初始化
}
// 交叉触发回调
callback = entries => {
console.log(entries)
// 防止插入瞬间再次触发离开交叉事件
if (entries[0].intersectionRatio === 0) return
this.pageNum += 1
this.loadMoreFn(this.pageNum)
}
// 关闭交叉监听
canclobserve() {
console.log('完毕')
this.observer.unobserve(this.obsNode)
}
// 设置首位节点交叉监听
hanldeObserve() {
this.observer.observe(this.obsNode)
}
// 初始化
init() {
// 创建 IntersectionObserver 实例子
this.observer = new IntersectionObserver(this.callback, {
root: this.rootNode || null, // 交叉窗口、根元素
rootMargin: '0px', // 交叉窗口边界扩展或者伸缩
threshold: [0.8] // 交叉占比(0~1),达到该比例才触发回调
})
this.hanldeObserve()
}
}
- 当回调调用插入元素,会将哨兵元素基础root,此时会再次触发交叉回调 if (entries[0].intersectionRatio === 0) return 通过此来防止这种情况
调用
let loadEx = new LoadMore({
root: 'content',
obs: 'bottom',
loadMoreFn: pageNum => {
// 最好一页关闭监听
if (pageNum * 10 > list.length) {
loadEx.canclobserve()
}
setTimeout(() => {
// 插入dom
let curPage = list.slice((pageNum - 1) * 10, pageNum * 10)
// 创建文档碎片
let frm = document.createDocumentFragment()
for (let i = 0; i < curPage.length; i++) {
let li = document.createElement('li')
li.innerHTML = curPage[i]
li.className = 'item-list'
frm.appendChild(li)
}
document.getElementById('list').appendChild(frm)
frm = null
}, Math.random() * 3000)
}
})
vue 自定义指令实现
Vue.directive('loadMore', {
bind: function(el, binding) {
let opation = binding.value
let div = document.createElement('div')
div.id = 'footGuard'
div.style.width = '100%'
div.style.height = '20px'
el.appendChild(div)
el.observer = new IntersectionObserver(
entries => {
if (entries[0].intersectionRatio === 0) return
opation()
},
{
root: el || null, // 交叉窗口、根元素
rootMargin: '100px', // 交叉窗口边界扩展或者伸缩
threshold: 0.5 // 交叉占比(0~1),达到该比例才触发回调
}
)
el.observer.observe(div)
},
inserted: function() {},
update: function() {},
componentUpdated: function() {},
unbind: function() {}
})
组件内使用
- {{item.name}}