【问题标题】:Detecting when user scrolls to bottom of div with React js使用 React js 检测用户何时滚动到 div 的底部
【发布时间】:2018-01-17 00:57:52
【问题描述】:

我有一个包含不同部分的网站。我正在使用 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);
  }
};

【问题讨论】:

标签: javascript reactjs


【解决方案1】:

更简单的方法是使用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 节点)。

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

【讨论】:

  • 来自scrollTop 网络文档:On systems using display scaling, scrollTop may give you a decimal value. 如果您想考虑浏览器窗口中不同级别的缩放,您可能需要获取 scrollTop 的上限来比较这些值。 node.scrollHeight - Math.ceil(node.scrollTop) === node.clientHeight
  • 很好的解决方案!为我省去了很多麻烦!谢谢
  • 出于某种原因,我的scrollTop 以“低”十进制值出现。例如 34.22233。出于这个原因,我使用Math.floor(scrollTop)。这似乎有点“hacky”。我会找到应该实施 ceil 而不是 floor 的情况吗?有更好的解决方案吗?也许Math.Round
  • Math.abs(scrollHeight - (scrollTop + clientHeight) &lt;= 1 怎么样。到目前为止对我来说效果很好。
  • 谢谢!你帮了我很多。比事件监听器更好的解决方案!
【解决方案2】:

你可以使用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);
  }
};

【讨论】:

  • 在组件卸载 (componentWillUnmount) 上删除事件 (document.removeEventListener) 也是一个好习惯
  • wrappedElement 为空。为什么?
  • @StavAlfi 因为你可能没有“标题”ID
【解决方案3】:

这是一个使用 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;

【讨论】:

    【解决方案4】:

    我们也可以通过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);
    

    【讨论】:

      【解决方案5】:

      这个答案属于 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 标记处

      【讨论】:

        【解决方案6】:

        将 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>
            );
        }
        

        【讨论】:

        • 我觉得referenceNoce应该定义为useRef而不是useState吧?
        【解决方案7】:

        在你的 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
            );
          }
        

        【讨论】:

          【解决方案8】:

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

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

          【讨论】:

            【解决方案9】:

            我在我的代码中使用了跟随

            .modify-table-wrap {
                填充顶部:50px;
                高度:100%;
                溢出-y:滚动;
            }
            
            

            并在目标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>
                        )
            
            

            【讨论】:

              【解决方案10】:

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

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

              【讨论】:

                【解决方案11】:

                在滚动 div 之后放置一个高度为 0 的 div。然后使用this custom hooks 来检测这个div是否可见。

                  const bottomRef = useRef();
                  const reachedBottom = useCustomHooks(bottomRef);
                
                  return(
                  <div>
                   {search resault}
                  </div>
                  <div ref={bottomRef}/> )
                

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

                【讨论】:

                  猜你喜欢
                  • 2011-09-10
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-04-16
                  • 2012-10-04
                  • 1970-01-01
                  相关资源
                  最近更新 更多