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

我有一个不同部分的网站。我使用 secment.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);
}
};
150085 次浏览

you can use el.getBoundingClientRect().bottom to check if the bottom has been viewed

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

An even simpler way to do it is with scrollHeight, scrollTop, and clientHeight.

Subtract the scrolled height from the total scrollable height. If this is equal to the visible area, you've reached the bottom!

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

In react, just add an onScroll listener to the scrollable element, and use event.target in the callback.

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

I found this to be more intuitive because it deals with the scrollable element itself, not the window, and it follows the normal React way of doing things (not using ids, ignoring DOM nodes).

You can also manipulate the equation to trigger higher up the page (lazy loading content/infinite scroll, for example).

We can also detect div's scroll end by using ref.

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

I know this has already been answered but, I think another good solution is to use what's already available out in the open source community instead of DIY. React Waypoints is a library that exists to solve this very problem. (Though don't ask me why the this problem space of determining if a person scrolls past an HTML element is called "waypoints," haha)

I think it's very well designed with its props contract and definitely encourage you to check it out.

Extending chandresh's answer to use react hooks and ref I would do it like this;

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

Add following functions in your React.Component and you're done :]

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

I used follow in my code

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


And add code in target 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>
)


This answer belongs to Brendan, let's make it functional

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

If the first div is not scrollable it won't work and onScroll didn't work for me in a child element like div after the first div so onScroll should be at the first HTML tag that has an overflow

Here's a solution using React Hooks and 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;

To evaluate whether my browser has scrolled to the bottom of a div, I settled with this solution:

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

Put a div with 0 height after your scrolling div. then use this custom hooks to detect if this div is visible.

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


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

reachedBottom will toggle to true if you reach bottom

The solution below works fine on most of browsers but has problem with some of them.

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

The better and most accurate is to use the code below which works on all browsers.

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

So the final code should be something like this

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