繁体   English   中英

使用 React js 检测用户何时滚动到 div 的底部

[英]Detecting when user scrolls to bottom of div with React js

我有一个包含不同部分的网站。 我正在使用 segment.io 来跟踪页面上的不同操作。 如何检测用户是否滚动到 div 的底部? 我尝试了以下操作,但它似乎是在我滚动页面时触发的,而不是当我到达 div 底部时触发的。

componentDidMount() {
  document.addEventListener('scroll', this.trackScrolling);
}

trackScrolling = () => {
  const wrappedElement = document.getElementById('header');
  if (wrappedElement.scrollHeight - wrappedElement.scrollTop === wrappedElement.clientHeight) {
    console.log('header bottom reached');
    document.removeEventListener('scroll', this.trackScrolling);
  }
};

一个更简单的方法是使用scrollHeightscrollTopclientHeight

从总可滚动高度中减去滚动高度。 如果这等于可见区域,则您已到达底部!

element.scrollHeight - element.scrollTop === element.clientHeight

在 React 中,只需为可滚动元素添加一个 onScroll 侦听器,并在回调中使用event.target

class Scrollable extends Component {

  handleScroll = (e) => {
    const bottom = e.target.scrollHeight - e.target.scrollTop === e.target.clientHeight;
    if (bottom) { ... }
  }

  render() {
    return (
      <ScrollableElement onScroll={this.handleScroll}>
        <OverflowingContent />
      </ScrollableElement>
    );
  }
}

我发现这更直观,因为它处理可滚动元素本身,而不是window ,并且它遵循正常的 React 做事方式(不使用 id,忽略 DOM 节点)。

您还可以操纵方程式以在页面上方触发(例如,延迟加载内容/无限滚动)。

您可以使用el.getBoundingClientRect().bottom来检查底部是否已被查看

isBottom(el) {
  return el.getBoundingClientRect().bottom <= window.innerHeight;
}

componentDidMount() {
  document.addEventListener('scroll', this.trackScrolling);
}

componentWillUnmount() {
  document.removeEventListener('scroll', this.trackScrolling);
}

trackScrolling = () => {
  const wrappedElement = document.getElementById('header');
  if (this.isBottom(wrappedElement)) {
    console.log('header bottom reached');
    document.removeEventListener('scroll', this.trackScrolling);
  }
};

我们还可以使用 ref 检测 div 的滚动结束。

import React, { Component } from 'react';
import {withRouter} from 'react-router-dom';
import styles from 'style.scss';

class Gallery extends Component{ 

  paneDidMount = (node) => {    
    if(node) {      
      node.addEventListener("scroll", this.handleScroll.bind(this));      
    }
  }

  handleScroll = (event) => {    
    var node = event.target;
    const bottom = node.scrollHeight - node.scrollTop === node.clientHeight;
    if (bottom) {      
      console.log("BOTTOM REACHED:",bottom); 
    }    
  }

  render() {
    var that = this;        
    return(<div className={styles.gallery}>
      <div ref={that.paneDidMount} className={styles.galleryContainer}>
        ...
      </div>

    </div>);   
  }
}

export default withRouter(Gallery);

这是使用 React Hooks 和 ES6 的解决方案:

import React, { useRef, useEffect } from 'react';

const MyListComponent = () => {
  const listInnerRef = useRef();

  const onScroll = () => {
    if (listInnerRef.current) {
      const { scrollTop, scrollHeight, clientHeight } = listInnerRef.current;
      if (scrollTop + clientHeight === scrollHeight) {
        // TO SOMETHING HERE
        console.log('Reached bottom')
      }
    }
  };

  return (
    <div className="list">
      <div className="list-inner" onScroll={() => onScroll()} ref={listInnerRef}>
        {/* List items */}
      </div>
    </div>
  );
};

export default List;

扩展 chandresh 的答案以使用 react hooks 和 ref 我会这样做;

import React, {useState, useEffect} from 'react';

export default function Scrollable() {
    const [referenceNode, setReferenceNode] = useState();
    const [listItems] = useState(Array.from(Array(30).keys(), (n) => n + 1));

    useEffect(() => {
        return () => referenceNode.removeEventListener('scroll', handleScroll);
    }, []);

    function handleScroll(event) {
        var node = event.target;
        const bottom = node.scrollHeight - node.scrollTop === node.clientHeight;
        if (bottom) {
            console.log('BOTTOM REACHED:', bottom);
        }
    }

    const paneDidMount = (node) => {
        if (node) {
            node.addEventListener('scroll', handleScroll);
            setReferenceNode(node);
        }
    };

    return (
        <div
            ref={paneDidMount}
            style={{overflowY: 'scroll', maxHeight: '400px'}}
        >
            <ul>
                {listItems.map((listItem) => <li>List Item {listItem}</li>)}
            </ul>
        </div>
    );
}

这个答案属于 Brendan,让我们让它发挥作用

export default () => {
   const handleScroll = (e) => {
       const bottom = e.target.scrollHeight - e.target.scrollTop === e.target.clientHeight;
       if (bottom) { 
           console.log("bottom")
       }
    }

  return (
     <div onScroll={handleScroll}  style={{overflowY: 'scroll', maxHeight: '400px'}}  >
        //overflowing elements here
   </div>
  )
}

如果第一个 div 不可滚动,它将不起作用,并且 onScroll 在第一个 div 之后的 div 等子元素中对我不起作用,因此 onScroll 应该位于第一个溢出的 HTML 标记处

在你的 React.Component 中添加以下函数,你就完成了:]

  componentDidMount() {
    window.addEventListener("scroll", this.onScroll, false);
  }

  componentWillUnmount() {
    window.removeEventListener("scroll", this.onScroll, false);
  }

  onScroll = () => {
    if (this.hasReachedBottom()) {
      this.props.onScrollToBottom();
    }
  };

  hasReachedBottom() {
    return (
      document.body.offsetHeight + document.body.scrollTop ===
      document.body.scrollHeight
    );
  }

我知道这已经得到了回答,但我认为另一个好的解决方案是使用开源社区中已有的内容而不是 DIY。 React Waypoints是一个为解决这个问题而存在的库。 (虽然不要问我为什么这个确定一个人是否滚动通过 HTML 元素的问题空间被称为“航点”,哈哈)

我认为它的道具合同设计得很好,绝对鼓励你去看看。

我在我的代码中使用了

.modify-table-wrap {
    padding-top: 50px;
    height: 100%;
    overflow-y: scroll;
}

并在目标js中添加代码

    handleScroll = (event) => {
        const { limit, offset } = this.state
        const target = event.target
        if (target.scrollHeight - target.scrollTop === target.clientHeight) {
            this.setState({ offset: offset + limit }, this.fetchAPI)
        }
    }
    return (
            <div className="modify-table-wrap" onScroll={this.handleScroll}>
               ...
            <div>
            )

为了评估我的浏览器是否已滚动到 div 的底部,我采用了以下解决方案:

const el = document.querySelector('.your-element');
const atBottom = Math.ceil(el.scrollTop + el.offsetHeight) === el.scrollHeight;

在滚动 div 后放置一个高度为 0 的 div。 然后使用这个自定义钩子来检测这个 div 是否可见。

  const bottomRef = useRef();
  const reachedBottom = useCustomHooks(bottomRef);

  return(
  <div>
   {search resault}
  </div>
  <div ref={bottomRef}/> )

如果到达底部, reachedBottom将切换为true

下面的解决方案在大多数浏览器上工作正常,但其中一些有问题。

element.scrollHeight - element.scrollTop === element.clientHeight

更好和最准确的方法是使用下面适用于所有浏览器的代码。

Math.abs(e.target.scrollHeight - e.target.clientHeight - e.target.scrollTop) < 1

所以最终的代码应该是这样的

const App = () => {
   const handleScroll = (e) => {
     const bottom = Math.abs(e.target.scrollHeight - e.target.clientHeight - e.target.scrollTop) < 1;
     if (bottom) { ... }
   }
   return(
     <div onScroll={handleScroll}>
       ...
     </div>
   )
}

这个答案属于布伦丹,但我能够以这种方式使用该代码。

window.addEventListener("scroll", (e) => {
        const bottom =
            e.target.scrollingElement.scrollHeight -
                e.target.scrollingElement.scrollTop ===
            e.target.scrollingElement.clientHeight;
        console.log(e);
        console.log(bottom);
        if (bottom) {
            console.log("Reached bottom");
        }
    });

而其他人可以通过e.target.scrollHeight直接访问目标内部,
我可以通过e.target.scrollingElement.scrollHeight实现同样的目标

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM