这个方法的主要应用是懒加载(lazyload),无限加载(infinte scroll)
基于浏览器窗口的判断 .getBoundingClientRect()
dom api .getBoundingClientRect()
可以获取元素距离页面顶部top的距离(top,bottom),还有距离页面左侧的距离(left,right)
因此我们监听滚动条事件,在滚动条事件里面通过 判断.getBoundingClientRect()
获得的参数就能够确定,元素是否进入了可视区域内.
我们只需要判断element的 bottom大于0,并且top小于页面可视高度,就表明元素已经进入可视区域
页面可视高度,可以用window.innerHeight获取,注意是包括了滚动条的高度. 还有一个outerHeight属性则是包括了浏览器窗口的高度.
这里我的需求是一个toc插件,判断目录滚动到那个标题了,代码实现如下
// 添加滚动条监听事件,随着滚动判断标题是否出现在可视区域
useEffect(() => {
function handleScrollToc() {
// 可视窗口的高度
const viewHeight = window.innerHeight
let currentIndex = 0
for (const index in headList) {
const clientRect = headList[index].node.getBoundingClientRect()
// |<—————————————————— 视口起点
// |——————rect.top—————|
// | |
// | rect.height |
// | |
// |—————rect.bottom———|
// 判断元素是否在可视区域内
if (clientRect.bottom >= 0 && clientRect.bottom < viewHeight) {
currentIndex = parseInt(index)
setActiveIndex(currentIndex)
break
}
}
}
window.addEventListener('scroll', handleScrollToc)
return () => {}
}, [headList])
基于滚动距离和元素到顶部的距离来判断
这里我们利用到dom元素的offsetTop属性,计算目标元素到父元素顶部的的距离.
因为offsetTop只是到父元素顶部的距离,而不一定等于到页面顶部的距离,我们可以用下面的方式把所有父元素的offsetTop相加,这样就能得到到页面顶部的距离
export function getElementToPageTop(el: any): number {
if (el.parentElement) {
return getElementToPageTop(el.parentElement) + el.offsetTop
}
return el.offsetTop
}
window的pageYoffset属性可以计算当滚动条滚动时document卷起的高度,也就等于scrollY吧.
我们考虑到两种边界情况,这样逻辑比较清晰.
首先是指定的元素顶部和浏览器可视区域的下边界重合的情况.
此时有等式
el到页面顶部的距离= window.innerHeight+window.scrollY
el到页面顶部的距离和window.innerHeight这两个参数是固定的
我们继续往下滚,那么window.scrollY就会增大,所以有
window.scrollY> el到页面顶部的距离 - window.innerHeight
继续滚动直到目标元素的下边界和浏览器的上边界重合,此时是元素离开可视窗口的情况.
此时同样也有一个等式,并且随着继续向下滚动,等式变成不等式...
通过这两个边界条件,就可以确定dom元素在可视窗口区域的区间.
方法二的浏览器兼容性不如方法一.所以一般用第一种.