仿“今日头条”效果的js无限滚动加载

效果

《今日头条》网页里页面向下滚动时,新闻会不断的追加。查看了 dom 元素,他们是把新请求到的新闻不断地追加到 ul 里。

思路

1. 判断网页是否滚动到了底部,并且当前没有请求在执行

2. 请求数据

3. 追加数据

细节

1. 可以规定距离页面底部多少距离算底部(这里用的是20),在这个区域内会请求数据

2. 用一个状态锁 isFetching 判断当前有没有请求正在执行,有请求在执行的话就不发请求了,以免数据混乱

3. 没有更多数据时的那个一次性定时器一定要清理

4. 用节流函数优化了滚动事件,规定300毫秒执行一次

完整代码

基于 React

index.scss

.container {
  width: 300px;
  margin: 0 auto;
  font-size: 20px;
  position: relative;

  .ul {
    list-style: none;
    margin: 0;
    padding: 0;
  }

  .item {
    height: 100px;
    margin-bottom: 10px;
    background-color: lightblue;

    &:last-child {
      margin-bottom: 0;
    }
  }
  .bottomTip {
    font-size: 12px;
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 20px;
    text-align: center;
    background-color: #ccc;
    color: #fff;
  }
}

index.jsx

import React, {Component} from 'react';
import './index.scss';
import request from '@/server/request';


export default class InfiniteScroll extends Component {
    constructor(props) {
        super(props);
        this.state = {
            list: [], //列表数据
            pageNo: 1,
            pageSize: 10,
            total: 32, //数据总数
            isFetching: false, //是否正在加载,时间锁
            noMore: false, //更多数据
        }

        this.timer = null;
        this.handleScrollThrottle = this.throttle(this.handleScroll, 300);
    }

    componentDidMount() {
        this.fetchData();
        window.addEventListener('scroll', this.handleScrollThrottle, false)
    }

    componentWillUnmount() {
        window.removeEventListener('scroll', this.handleScrollThrottle);
    }

    // 是否滚动到页面底部
    isScrollToPageBottom() {
        const offsetHeight = document.documentElement.offsetHeight; //文档高度
        const innerHeight = window.innerHeight; //浏览器窗口的高
        const scrollY = window.scrollY; //页面滚动条的垂直距离

        return offsetHeight - scrollY - innerHeight < 20;
    }

    handleScroll = () => {
        const { pageNo, pageSize, total, isFetching } = this.state;

        // 如果当前不是在加载中,并且滚动到距离页面底部,则加载数据
        if (!isFetching && this.isScrollToPageBottom()) {
            if (Math.ceil(total / pageSize) > pageNo) {
                this.setState({
                    pageNo: this.state.pageNo + 1,
                }, this.fetchData);
            } else {
                // 清除上一次的定时器,防止上一次定时器对底部提示文案的隐藏效果影响到本次
                clearTimeout(this.timer);
                this.setState({
                    noMore: true
                }, () => {
                    this.timer = setTimeout(() => {
                        this.setState({
                            noMore: false
                        })
                    }, 1500)
                })
            }
        }
    }

    fetchData = () => {
        this.setState({
            isFetching: true
        }, () => {
            request('/api/getItems', {pageNo: this.state.pageNo}).then(res => {
                this.setState({
                    list: [...this.state.list, ...res],
                    isFetching: false
                });
            })
        })
    }

    // 节流函数
    throttle = (func,delay) => {
            var timer = null;
         
            return () => {
                var context = this;
                var args = arguments;
                if(!timer){
                    timer = setTimeout(function(){
                        func.apply(context,args);
                        timer = null;
                    },delay);
                }
            }
    }

    render () {
        const { list, isFetching, noMore } = this.state;

        return (
            <div className='container'>
                <ul className='ul'>
                    {
                        list.map(item => <li key={item.id} className='item'>{item.id}</li>)
                    }
                </ul>
                {
                    isFetching ? <div className='bottomTip'>加载中...</div> : null
                }
                {
                    noMore ? <div className='bottomTip'>没有更多数据了</div> : null
                }
            </div>
        )
    }
}

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值