I have a website with different sections. I am using segment.io to track different actions on the page. How can I detect if a user has scrolled to the bottom of a div? I have tried the following but it seems to be triggered as soon as I scroll on the page and not when I reached the bottom of the 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);
}
};
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).
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);
}
};
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;