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

34 javascript reactjs

我有一个不同部分的网站.我正在使用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);
  }
};
Run Code Online (Sandbox Code Playgroud)

Bre*_*ill 49

更简单的方法是使用scrollHeight,scrollTopclientHeight.

您只需从总可滚动高度中减去滚动高度.如果这等于可见区域,那么你就到了底部!

element.scrollHeight - element.scrollTop === element.clientHeight
Run Code Online (Sandbox Code Playgroud)

在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>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 怎么样`Math.abs(scrollHeight - (scrollTop + clientHeight) &lt;= 1`。到目前为止对我来说效果很好。 (6认同)
  • 在`scrollTop`网络文档中:`在使用显示比例缩放的系统上,scrollTop可能会为您提供一个十进制值。`如果要考虑浏览器窗口中不同的缩放级别,则可能需要获取scrollTop的上限以进行比较价值观。`node.scrollHeight-Math.ceil(node.scrollTop)=== node.clientHeight` (3认同)

eww*_*ink 39

你可以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);
  }
};
Run Code Online (Sandbox Code Playgroud)

  • 在组件卸载(`componentWillUnmount`)上删除事件(`document.removeEventListener`)也是一种很好的做法. (5认同)
  • `wrappedElement` 为空。为什么? (2认同)
  • @StavAlfi 因为你可能没有“标题”id (2认同)

All*_*ney 14

这是使用 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;
Run Code Online (Sandbox Code Playgroud)


小智 6

我们还可以使用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);
Run Code Online (Sandbox Code Playgroud)


小智 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>
    );
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为“referenceNoce”应该由“useRef”而不是“useState”定义,对吧? (2认同)

Zah*_*sal 6

在你的 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
    );
  }
Run Code Online (Sandbox Code Playgroud)


Mad*_*eka 6

这个答案属于 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>
  )
}
Run Code Online (Sandbox Code Playgroud)

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