1

I'm looking for js solution that detects when user has scrolled to the bottom of the div with overflow: auto.

There are plenty of solutions here on SO that describe how to achieve it with onscroll event, but I was wondering if this can be done this with newer technology like IntersectionObserver that doesn’t require attaching the scroll event.

Runnick
  • 453
  • 1
  • 9
  • 24
  • 1
    Possible duplicate of [JS: event listener for when element becomes visible?](https://stackoverflow.com/questions/1462138/js-event-listener-for-when-element-becomes-visible) – Rafael Herscovici Oct 03 '18 at 12:25

4 Answers4

3

Use IntersectionObserver For this

const onScrollToBottom = document.getElementById('on-scroll-to-bottom')

const onIntersection = ([{isIntersecting, target}]) =>
  isIntersecting && (target.style.backgroundColor = 'green');

const io = new IntersectionObserver(onIntersection, {threshold: 1})

io.observe(onScrollToBottom)
section {
  color: white;
}

#on-scroll-to-bottom {
  margin-top: 100vh;
  min-height: 50vh;
  background-color: red;
}

#visible {
  height: 90vh;
  background-color: blue;
}
<section id="visible">Visible Section</section>
<section id="on-scroll-to-bottom">On Scroll to Bottom</section>
Benny Powers
  • 3,677
  • 3
  • 16
  • 35
  • Thanks! How easy is it to remove style when bottom is not visible? – Runnick Oct 03 '18 at 13:34
  • 1
    You can pass multiple values to `threshold` for example: `threshold: [0, 0.5, 1]`, then check the `intersectionRatio` property on `entry`: `const onIntersection = ([{intersectionRatio, target}]) => target.style.backgroundColor = intersectionRatio > 0.5 ? 'green' : 'red'` – Benny Powers Oct 03 '18 at 13:45
  • This won't work if an element's height is larger than the viewport height. – catamphetamine Dec 04 '19 at 09:51
  • @asdfasdfads it does. what specifically isn't working for you? – kano Dec 17 '19 at 17:04
3

Simply using IntersectionObserver like the "accepted" answer suggests is not going to work:

const io = new IntersectionObserver(onIntersection, {threshold: 1})

If the element's height is greater than the viewport height then the threshold won't ever be 1. Instead, I found a better solution for variable-height content: insert a 1px sub-element inside the element being tracked and then track the sub-element instead of the element itself via IntersectionObserver. This way the onIntersection callback will be called when a user scrolls down to the bottom of the element.

catamphetamine
  • 3,245
  • 25
  • 24
2

To solve the issue brought up by @catamphetamine that if the element is larger than the viewport it will never reach threshold 1, you could flatten the parent element's bounds to just the bottom of itself and then observe for threshold: 0 and not intersecting instead:

const io = new IntersectionObserver(onIntersection, {
        rootMargin: '-100% 0px 0px 0px', 
        threshold: 0
    })
Mordechai
  • 13,232
  • 1
  • 32
  • 69
0

You can use entry.boundingClientRect which tells bottom is in viewport or not. If bottom is less than 0, which means above the viewport, also if bottom is greater than window size, which means below the viewport, it's not in view port.

Also threshold check must be on each scroll when it's in viewport, slicing to each 10%.

    const observer = new IntersectionObserver(entries => {
      entries.forEach(entry => { 
        const bcr = entry.boundingClientRect;
        const isBottomVisible = (bcr.bottom < window.innerHeight) && bcr.bottom;
        console.log(bcr.top, bcr.bottom, window.innerHeight, {isBottomVisible});
      });
    }, {threshold: Array(11).fill().map( (_, i) => i*.1)});
    observer.observe(this._templateContainer );
allenhwkim
  • 25,529
  • 15
  • 80
  • 114