起因
之前自己在使用这种网站时,经常看到无限加载的效果。
今天正好看到了getBoundingClientRect
这个Api,就想着试试看如何实现Infinite scroll
的效果。
原理
在需要无限加载的列表底部,埋下一个隐藏元素。
当不断滑动时,隐藏元素将出现在视窗(viewport)里,也就意味着当前浏览的列表已经到底部了。
这时候就需要进行列表加载。
大概的HTML结构如下:
1 | <div> |
也就是:滑动列表 => 隐藏的无限加载指示器出现在视图 => 开始加载
那么重点就是检测隐藏的无限加载指示器是否出现在视图窗口。
还好,我们有getBoundingClientRect
这个Api。
getBoundingClientRect
通过查阅MDN,得知:
Element.getBoundingClientRect()方法返回元素的大小及其相对于视口的位置。而除了 width 和 height 外的属性都是相对于视口的左上角位置而言的。
至于兼容性,一片绿,可以放心使用。
DOMRect 对象
getBoundingClientRect()方法的返回值是一个 DOMRect 对象,这个对象是由该元素的 getClientRects() 方法返回的一组矩形的集合, 即:是与该元素相关的CSS 边框集合 。
对象的属性如下图所示:
其中的 top, left, bottom, right
均是元素自身相对于视图左上角而言的。
就top, left
属性而言,很好理解。而bottom, right
则一开始搞的有点懵,后面通过devtools观察,发现bottom
是元素的最底部相对于视图窗口左上角而言的,而right
则是元素的最右侧相对于视图窗口左上角而言的。
其中right-left
为元素的宽度,bottom - top
则是元素的高度。
检测元素是否出现于视图窗口中
在这里,有两种情况,一个是元素是否出现于视图窗口中,另一种则是元素是否完全出现于视图窗口中。
两种情况的区别在于一个是部分出现,一个是完全出现。
下面我把两种情况都写出来:
- 部分出现在视图窗口中
1 | function checkIsPartialVisible (element) { |
- 全部出现于视图窗口中:
1 | function checkIsTotalVisible (element) { |
那么问题来了:我们到底选用那种呢?
从无限加载这个业务场景出发,埋在列表最下边的加载触发器都非常小且不可见,因此推荐选用第二种,也就是完全出现于视图窗口的方式。
至于第一种,更适合检测该元素是否已经出现在视图窗口,但并不要求全部出现的情况。
实战
具体可以看我在jsfiddle上写的demo:
无限加载实例
后续
后续更多的则是一些性能优化的事情,比如debounce或者throttle来减少scroll事件调用次数,加入ajax加载,loading indicator等。
那些都是属于具体的业务范围了,这儿不做讨论。