js、react、vue里滚动到底部加载数据和滚动到顶部实现(js监听滚动事件,到底部请求接口)

前言:

最近在做一个项目,一个列表展示原本是分页器,点击下一页,请求接口。但是业务说需要改成滑倒底部,加载数据。

一、实现过程

整体思路就是 获取 滚动元素的 scrollHeight(元素内容的总高度)、clientHeight(元素的可见高度)、scrollTop(元素滚动条垂直滚动的距离)

  • scrollHeight表示元素内容的总高度,包括溢出部分;
  • clientHeight表示元素的可见高度,不包括滚动条、边框和外边距;
  • scrollTop表示元素滚动条垂直滚动的距离,即内容顶部相对于可见区域顶部的偏移量。

然后 在滚动事件里 判断 满足scrollTop+clientHeight >=scrollHeight  即可调用接口。

1.html+js

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<style>
    /* * {
        padding: 0;
        margin: 0;
    } */

    ul>li {
        width: 300px;
        height: 100px;
        list-style: none;
        border: 1px solid red;
        text-align: center;
    }

    #btnTop {
        position: fixed;
        bottom: 50px;
        right: 50px;
    }
</style>

<body>
    <div class="a">
        <ul id="box">

        </ul>
        <div>
            <button id="btnTop">滚动到顶部</button>
        </div>
    </div>
</body>
<script>
    //获取滚动条被卷进去的高
    function getScrollTop() {
        var scrollTop = 0, bodyScrollTop = 0, documentScrollTop = 0;
        if (document.body) {
            bodyScrollTop = document.body.scrollTop;
        }
        if (document.documentElement) {//兼容ie
            documentScrollTop = document.documentElement.scrollTop;
        }
        scrollTop = (bodyScrollTop - documentScrollTop > 0) ? bodyScrollTop : documentScrollTop;
        return scrollTop;
    }
    //获取可滚动高度
    function getScrollHeight() {
        var scrollHeight = 0, bodyScrollHeight = 0, documentScrollHeight = 0;
        if (document.body) {
            bodyScrollHeight = document.body.scrollHeight;
        }
        if (document.documentElement) {//兼容ie
            documentScrollHeight = document.documentElement.scrollHeight;
        }
        scrollHeight = (bodyScrollHeight - documentScrollHeight > 0) ? bodyScrollHeight : documentScrollHeight;
        return scrollHeight;
    }
    //获取可见区域高
    function getWindowHeight() {
        var windowHeight = 0;
        if (document.compatMode == "CSS1Compat") {//兼容ie
            windowHeight = document.documentElement.clientHeight;
        } else {
            windowHeight = document.body.clientHeight;
        }
        return windowHeight;
    }
    function add(val) { //这里实际是请求接口比如到底部请求下一页接口
        let box = document.getElementById('box');
        for (var i = 0; i < val; i++) {
            let li = document.createElement('li');
            li.innerText = i;
            box.appendChild(li)
        }
    }
    add(10)
    //事件监听
    window.onscroll = function () {
        //滚动条被卷进去的高+可见区域高=可滚动高度说明到底部了
        console.log(getScrollTop() + getWindowHeight(), getScrollHeight())
        //到底部请求接口 Math.round四舍五入因为太多位会导致精度缺失
        if (Math.round(getScrollTop() + getWindowHeight()) >= getScrollHeight()) {
            add(10)
        }
    };
    let btn = document.getElementById('btnTop');
    btn.onclick = function () { // 页面滚动到顶部
    
        // 方法一
        document.body.scrollTop = document.documentElement.scrollTop = 0

        // 方法二
        //document.body.scrollIntoView()
        // scrollIntoView 是元素也有的方法, 可以用在页面元素上,例如
        // document.getElementById('id').scrollIntoView()
    }
</script>

</html>

2.react实现

import React, { Component } from 'react';

class Index extends Component {
    constructor(props) {
        super(props)
        this.state = {
            data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        }
    }
    componentDidMount() {
        window.addEventListener("scroll", this.add);//监听滚动事件
    }
    add = () => {
        let { data, page } = this.state;
        console.log(Math.round(this.getScrollTop() + this.getWindowHeight()), this.getScrollHeight())
        if (Math.round(this.getScrollTop() + this.getWindowHeight()) >= this.getScrollHeight() - 1) {
            //项目中在这里面请求接口
            data.push(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
            this.setState({
                data
            })
        }


    }
    //获取滚动条被卷进去的高
    getScrollTop = () => {
        var scrollTop = 0, bodyScrollTop = 0, documentScrollTop = 0;
        if (document.body) {
            bodyScrollTop = document.body.scrollTop;
        }
        if (document.documentElement) {//兼容ie
            documentScrollTop = document.documentElement.scrollTop;
        }
        scrollTop = (bodyScrollTop - documentScrollTop > 0) ? bodyScrollTop : documentScrollTop;
        return scrollTop;
    }
    //获取可滚动高度
    getScrollHeight = () => {
        var scrollHeight = 0, bodyScrollHeight = 0, documentScrollHeight = 0;
        if (document.body) {
            bodyScrollHeight = document.body.scrollHeight;
        }
        if (document.documentElement) {//兼容ie
            documentScrollHeight = document.documentElement.scrollHeight;
        }
        scrollHeight = (bodyScrollHeight - documentScrollHeight > 0) ? bodyScrollHeight : documentScrollHeight;
        return scrollHeight;
    }
    //获取可见区域高
    getWindowHeight = () => {
        var windowHeight = 0;
        if (document.compatMode == "CSS1Compat") {//兼容ie
            windowHeight = document.documentElement.clientHeight;
        } else {
            windowHeight = document.body.clientHeight;
        }
        return windowHeight;
    }
    componentWillUnmount(){
        window.removeEventListener("scroll",this.add);//销毁监听事件防止内存泄漏
    }
    render() {
        let { data } = this.state;
        return (
            <div>
                <ul>
                    {
                        data.map((item, index) => {
                            return (
                                <li style={
   
   { height: "200px" }} key={index}>{item}</li>
                            )
                        })
                    }
                </ul>
            </div>
        );
    }
}

export default Index

3.react升级版本 

 为什么升级呢,因为 我发现上一个版本不适合所有场景(基于body的)。上面的版本知识和 没有指定 滚动dom的情况。而大部分使用场景都是需要指定某个元素的。所以我做了改动,也就是这个版本更灵活。

1.class版本
import React, { FC, useEffect, useRef, useState, Component } from 'react'
import styles from './Home.module.less'
class Home1 extends Component {
  constructor(props) {
    super(props)
    this.state = {
      data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
      dom: null
    }
  }
  componentDidMount() {
    let dom = document.getElementById("list-box");
    dom.addEventListener("scroll", this.add);//监听滚动事件
    this.setState({
      dom
    })
  }
  add = () => {
    let { data, page } = this.state;
    console.log(Math.round(this.getScrollTop() + this.getClientHeight()), this.getScrollHeight())
    if (Math.round(this.getScrollTop() + this.getClientHeight()) >= this.getScrollHeight() - 1) {
      //项目中在这里面请求接口
      let arr=[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
      let data1 = data.concat(arr);
      this.setState({
        data: data1
      })
    }


  }
  //获取滚动条被卷进去的高
  getScrollTop = () => {
    const { dom } = this.state;
    let scrollTop = 0;
    if (typeof dom.scrollTop !== "undefined") {
      // 如果浏览器支持直接获取scrollTop属性
      scrollTop = dom.scrollTop;
    } else if (typeof dom.pageYOffset !== "undefined") {
      // 如果浏览器支持pageYOffset属性
      scrollTop = dom.pageYOffset;
    } else if (typeof dom.scrollY !== "undefined") {
      // 如果浏览器支持scrollY属性
      scrollTop = dom.scrollY;
    } else if (typeof document.documentElement.scrollTop !== "undefined") {
      // 兼容IE浏览器的获取scrollTop属性
      scrollTop = document.documentElement.scrollTop;
    } else if (typeof document.body.scrollTop !== "undefined") {
      // 兼容IE浏览器的获取scrollTop属性
      scrollTop = document.body.scrollTop;
    }
    return scrollTop;
  }
  //获取可滚动高度
  getScrollHeight = () => {
    const { dom } = this.state;
    var scrollHeight = 0;
    if (typeof dom.scrollHeight !== "undefined") {
      // 如果浏览器支持直接获取scrollHeight属性
      scrollHeight = dom.scrollHeight;
    } else if (typeof dom.offsetHeight !== "undefined") {
      // 如果浏览器支持offsetHeight属性
      scrollHeight = dom.offsetHeight;
    } else if (typeof dom.clientHeight !== "undefined") {
      // 如果浏览器支持clientHeight属性
      scrollHeight = dom.clientHeight;
    }
    return scrollHeight;

  }
  //获取可见区域高
  getClientHeight = () => {
    const { dom } = this.state;
    var clientHeight = 0;
    if (typeof dom.clientHeight !== "undefined") {
      // 如果浏览器支持直接获取clientHeight属性
      clientHeight = dom.clientHeight;
    } else if (typeof dom.offsetHeight !== "undefined") {
      // 如果浏览器支持offsetHeight属性
      clientHeight = dom.offsetHeight;
    }
    return clientHeight;
  }
  componentWillUnmount() {
    const { dom } = this.state;
    dom.removeEventListener("scroll", this.add);//销毁监听事件防止内存泄漏
  }
  render() {
    let { data } = this.state;
    return (
      <div>
        <ul className={styles.listBox} id='list-box'>
          {
            data.map((item, index) => {
              return (
                <li className={styles.listItem} key={index}>{item}</li>
              )
            })
          }
        </ul>
      </div>
    );
  }
}

export default Home1;

样式:

* {
    margin: 0;
    padding: 0;
}

.listBox {
    width: 300px;
    height: 800px;
    border: 1px solid red;
    overflow: auto;

}

.listItem {
    width: 100%;
    height: 80px;
    border-bottom: 1px solid blue;
}
2.hook版本
import React, { FC, useEffect, useRef, useState, Component } from 'react'
import styles from './Home.module.less'
const Home1 = () => {
  const [data, setData] = useState([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
  

  useEffect(() => {
    let dom = document.getElementById("list-box");
    dom.addEventListener("scroll", add);//监听滚动事件
    return () => {
      // 组件卸载时,移除滚动事件监听器
      dom.removeEventListener('scroll', add);
    };
  }, [])
  

 
  const add = () => {
    //console.log(Math.round(getScrollTop() + getClientHeight()), getScrollHeight())
    if (Math.round(getScrollTop() + getClientHeight()) >= getScrollHeight()-1) {
      //项目中在这里面请求接口
      let data1 = data.push(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
      setData(prevData => prevData.concat(data1));
    }


  }

  //获取滚动条被卷进去的高
  const getScrollTop = () => {
    let dom = document.getElementById("list-box");
    let scrollTop = 0;
    if (typeof dom.scrollTop !== "undefined") {
      // 如果浏览器支持直接获取scrollTop属性
      scrollTop = dom.scrollTop;
    } else if (typeof dom.pageYOffset !== "undefined") {
      // 如果浏览器支持pageYOffset属性
      scrollTop = dom.pageYOffset;
    } else if (typeof dom.scrollY !== "undefined") {
      // 如果浏览器支持scrollY属性
      scrollTop = dom.scrollY;
    } else if (typeof document.documentElement.scrollTop !== "undefined") {
      // 兼容IE浏览器的获取scrollTop属性
      scrollTop = document.documentElement.scrollTop;
    } else if (typeof document.body.scrollTop !== "undefined") {
      // 兼容IE浏览器的获取scrollTop属性
      scrollTop = document.body.scrollTop;
    }
    return scrollTop;
  }
  //获取可滚动高度
  const getScrollHeight = () => {
    let dom = document.getElementById("list-box");
    var scrollHeight = 0;
    if (typeof dom.scrollHeight !== "undefined") {
      // 如果浏览器支持直接获取scrollHeight属性
      scrollHeight = dom.scrollHeight;
    } else if (typeof dom.offsetHeight !== "undefined") {
      // 如果浏览器支持offsetHeight属性
      scrollHeight = dom.offsetHeight;
    } else if (typeof dom.clientHeight !== "undefined") {
      // 如果浏览器支持clientHeight属性
      scrollHeight = dom.clientHeight;
    }
    return scrollHeight;

  }
  //获取可见区域高
  const getClientHeight = () => {
    let dom = document.getElementById("list-box");
    var clientHeight = 0;
    if (typeof dom.clientHeight !== "undefined") {
      // 如果浏览器支持直接获取clientHeight属性
      clientHeight = dom.clientHeight;
    } else if (typeof dom.offsetHeight !== "undefined") {
      // 如果浏览器支持offsetHeight属性
      clientHeight = dom.offsetHeight;
    }
    return clientHeight;
  }

  return (
    <div>
      <ul className={styles.listBox} id='list-box'>
        {
          data.map((item, index) => {
            return (
              <li className={styles.listItem} key={index}>{item}</li>
            )
          })
        }
      </ul>
    </div>
  )
}
export default Home1;

   样式:

* {
    margin: 0;
    padding: 0;
}

.listBox {
    width: 300px;
    height: 800px;
    border: 1px solid red;
    overflow: auto;

}

.listItem {
    width: 100%;
    height: 80px;
    border-bottom: 1px solid blue;
}

4.vue里

vue里 以上代码理论上也能使用,把函数放到 methods里 变量放到data里(vue2)。

vue3的话就按照 ref写响应式变量,const 写函数。

如果 documnet.getxx不好用可以使用 vue 里的ref ,react同理。

猜你喜欢

转载自blog.csdn.net/weixin_44058725/article/details/114674230