15

I am currently using the intersection observer to detect when an element leaves the viewport. It is setup like this:

const el = document.querySelector('#el')
const observer = new window.IntersectionObserver(([entry]) => {
  if (entry.isIntersecting) {
    console.log('LEAVE')
    return
  }
  console.log('ENTER')
}, {
  root: null,
  threshold: 0,
})

observer.observe(el)

But I also want to know whether the element is above or below the viewport. Is there a way achieve this?

bravokiloecho
  • 1,413
  • 5
  • 22
  • 39
  • after inspecting in the console, the **entry** object has **a lot** of properties , by example `entry.target.offsetHeight` ... not a solution, but maybe a lead – gui3 Nov 21 '19 at 17:53

3 Answers3

34

seems like (with your code) the entry.boundingClientRect.top value is positive when the element is below and negative when the element is above the screen

check it here : https://codepen.io/gui3/pen/VwwRORL

_ edit _ after many tries, this definitively works

const observer = new window.IntersectionObserver(([entry]) => {
  console.log(entry.boundingClientRect.top)
  if (entry.isIntersecting) {
    console.log('Enter')
    position("VISIBLE") // do things if visible
    return
  }
  console.log('Leave')
  if (entry.boundingClientRect.top > 0) {
    position("BELOW") // do things if below
  } else {
    position("ABOVE") // do things if above
  }
}, {
  root: null,
  threshold: 0,
})
gui3
  • 1,711
  • 14
  • 30
  • 1
    this works great, but what if you want to do things if the intersection is right in the MIDDLE of a div, as opposed to all the way above or below? – Jon Feb 02 '22 at 22:07
  • @Jon interesting question, here is a working example https://codepen.io/gui3/pen/abEwReE the solution was to set the option `rootMargin: "-50% -50%"` AND to specify the scroll parent div as root – gui3 Mar 29 '22 at 18:42
  • 1
    @gui3 beware that `rootMargin` follows the shorthand for `margin`. The above is equivalent to `rootMargin: "-50%"` which deducts 50% from all sides. I think this focuses on a single pixel right at the center of the viewport. If things are passing by on the sides, they won't intersect. In certain use cases, you may need `rootMargin: "-50% 0px"` (top and bottom -50%, right and left 0px). – ADTC Jan 16 '23 at 18:36
  • This does not work accurately. If you navigate to an element above the box you are observing from an element below the box you are observing (for example via an anchor link), the page jumps to the new location, but this check keeps saying the old value (as the observer never gets triggered) Example: https://codepen.io/ferrybig/pen/GRXjROa – Ferrybig Feb 24 '23 at 12:19
1

A robust method of checking whether the observed target went below or above the root may be to compare the intersectionRect with the rootBounds.

  • First check that isIntersecting == false, then check the following:

    • If intersectionRect.bottom == rootBounds.bottom then the target went below the root.

    • If intersectionRect.top == rootBounds.top then the target went above the root.

Both are unlikely to be true at the same time.

ADTC
  • 8,999
  • 5
  • 68
  • 93
0

Checking entry.boundingClientRect.top works only if you don't set rootMargin value as the option in IntersectionObserver. You should check entry.isIntersecting instead.

If you need to check if the element has passed viewport and intersected, use this:

isIntersecting || boundingClientRect.top < 0
John Winston
  • 1,260
  • 15
  • 30