美文网首页js css htmljavascript
js实现滚动触底加载事件

js实现滚动触底加载事件

作者: 扶得一人醉如苏沐晨 | 来源:发表于2023-03-12 10:02 被阅读0次

    一、html和css

    可视区固定500px,设置overflow-y: auto 来实现滚动。

    1.1、html

    <template>
      <div class="scroll"
           ref='scroll'
           @scroll="onScroll">
        <div class="crad"
             v-for="i in 10"
             :key="i"></div>
      </div>
    </template>
    

    1.2、css

    <style lang='scss' scoped>
    .scroll {
      overflow: auto;
      height: 500px;
      .crad {
        height: 200px;
        margin-top: 10px;
        background-color: red;
      }
    }
    </style>
    

    1.3、效果如下

    image.png

    二、实现思路

    触发的条件是: 可视高度 + 滚动距离 >= 实际高度。例子我会使用vue来实现,和原生实现是一样的。

    • 可视高度(offsetHeight):通过 dom 的 offsetHeight获得,表示区域固定的高度。这里我推荐通过 getBoundingClientRect()来获取高度,因为使用前者会引起浏览器回流,造成一些性能问题。
    • 滚动高度(scrollTop):滚动事件中通过 e.target.scrollTop获取,表示滚动条距离顶部的px
    • 实际高度(scrollHeight):通过dom 的 scrollHeight获得,表示区域内所有内容的高度(包括滚动距离),也就是实际高度

    2.1、基础实现

    onScroll(e) {
        let scrollTop = e.target.scrollTop
        let scrollHeight = e.target.scrollHeight
        let offsetHeight = Math.ceil(e.target.getBoundingClientRect().height)
        let currentHeight = scrollTop + offsetHeight
        if (currentHeight >= scrollHeight) {
            console.log('触底')
        }
    }
    

    2.2、添加距离底部多少距离触发

    现在我们希望是离底部一定距离就触发事件,而不是等到完全触底。如果你做过小程序,这和onReachBottom差不多的意思。

    • 声明一个离底部的距离变量reachBottomDistance

    • 这时候触发条件:可视高度 + 滚动距离 + reachBottomDistance >= 实际高度

    export default {
      data(){
        return {
          reachBottomDistance: 100
        }
      },
      methods: {
        onScroll(e) {
            let scrollTop = e.target.scrollTop
            let scrollHeight = e.target.scrollHeight
            let offsetHeight = Math.ceil(e.target.getBoundingClientRect().height)
            let currentHeight = scrollTop + offsetHeight + this.reachBottomDistance
            if (currentHeight >= scrollHeight) {
                console.log('触底')
            }
        }
      }
    }
    

    2.3、再次优化

    在距离底部100px时成功触发事件,但由于100px往下的区域是符合条件的,会导致一直触发,这不是我们想要的。
    接下来做一些处理,让其进入后只触发一次:

    export default {
      data(){
        return {
          isReachBottom: false,
          reachBottomDistance: 100
        }
      },
      methods: {
        onScroll(e) {
            let scrollTop = e.target.scrollTop
            let scrollHeight = e.target.scrollHeight
            let offsetHeight = Math.ceil(e.target.getBoundingClientRect().height)
            let currentHeight = scrollTop + offsetHeight + this.reachBottomDistance
    
            if(currentHeight < scrollHeight && this.isReachBottom){
              this.isReachBottom = false
            }
            if(this.isReachBottom){
              return
            }
            if (currentHeight >= scrollHeight) {
              this.isReachBottom = true
              console.log('触底')
            }
        }
      }
    }
    

    2.4、最终优化

    实时去获取位置信息稍微会损耗性能,我们应该把不变的缓存起来,只实时获取可变的部分

    export default {
      data(){
        return {
          isReachBottom: false,
          reachBottomDistance: 100
          scrollHeight: 0,
          offsetHeight: 0,
        }
      },
      mounted(){
        // 页面加载完成后  将高度存储起来
        let dom = this.$refs['scroll']
        this.scrollHeight = dom.scrollHeight
        this.offsetHeight = Math.ceil(dom.getBoundingClientRect().height)
      },
      methods: {
        onScroll(e) {
            let scrollTop = e.target.scrollTop
            let currentHeight = scrollTop + this.offsetHeight + this.reachBottomDistance
            //第一次未触底之前都不触发
            if(currentHeight < this.scrollHeight && this.isReachBottom){
              this.isReachBottom = false
            }
            if(this.isReachBottom){
              return
            }
            if (currentHeight >= this.scrollHeight) {
              this.isReachBottom = true
              console.log('触底')
            }
        }
      }
    }
    

    三、利用触底事件实现滚动分页加载

    <template>
      <div class="scroll"
           ref='scroll'
           @scroll="onScroll">
        <div class="crad"
             v-for="item in showList"
             :key='item.id'></div>
    
        <p v-show="status=='loading'">加载中</p>
        <p v-show="status=='nomore'">没有更多了</p>
      </div>
    </template>
    
    <script>
    import { fetchList } from '@/api/index';
    export default {
      data () {
        return {
          isReachBottom: false,
          reachBottomDistance: 100,
          scrollHeight: 0,
          offsetHeight: 0,
          // 展示区list
          showList: [],
          pageIndex: 1,
          pageSize: 20,
          totalPage: 0,
          status: 'nomore'
        };
      },
      mounted () {
        this.getDataList()
      },
      methods: {
        onScroll (e) {
          let scrollTop = e.target.scrollTop;
          let currentHeight = scrollTop + this.offsetHeight + this.reachBottomDistance;
          //第一次未触底之前都不触发
          if (currentHeight < this.scrollHeight && this.isReachBottom) {
            this.isReachBottom = false;
          }
          if (this.isReachBottom) {
            return;
          }
          if (currentHeight >= this.scrollHeight) {
            this.isReachBottom = true;
            console.log('触底');
            this.loadMore();
          }
        },
        loadMore () {
          let { totalPage, pageIndex, pageSize } = this;
          if (totalPage > pageIndex * pageSize) {
            this.pageIndex++;
            this.getDataList();
            this.status = 'loading';
          } else {
            this.status = 'nomore';
          }
        },
        // 每次数据更新后重新计算高度
        calcHeight () {
          this.$nextTick(() => {
            // 页面加载完成后  将高度存储起来
            let dom = this.$refs['bottom'];
            this.scrollHeight = dom.scrollHeight;
            this.offsetHeight = Math.ceil(dom.getBoundingClientRect().height);
          });
        },
        getDataList () {
          fetchList({ current: this.pageIndex, size: this.pageSize }).then((res) => {
            let list = res.data.data.list;
            this.totalPage = res.data.data.totalPage;
            this.showList = this.showList.concat(list);
            // 每次数据更新后重新计算高度
            this.calcHeight();
            if (this.totalPage > this.pageIndex * this.pageSize) {
              this.status = 'loading';
            } else {
              this.status = 'nomore';
            }
          });
    
        }
      }
    }
    </script>
    
    <style lang='scss' scoped>
    .scroll {
      overflow: auto;
      height: 500px;
      .crad {
        height: 200px;
        margin-top: 10px;
        background-color: red;
      }
    }
    </style>
    

    相关文章

      网友评论

        本文标题:js实现滚动触底加载事件

        本文链接:https://www.haomeiwen.com/subject/qubaldtx.html