I had a situation where I wanted to visually introduce a footer element recently when a certain element was passed in the viewport. Instead of using scroll events I had opted to use the newer IntersectionObserver (finally we are getting broad browser support thanks to iOS 12.2 and Safari in macOS 10.14.4).

Anyway, there are some good tutorials already on the fundamentals of the IntersectionObserver. WebKit’s own https://webkit.org/blog/8582/intersectionobserver-in-webkit/ being one good example.

However, what doesn’t seem well covered is dealing with the direction of how the IntersectionObserver has been triggered.

I solved this by comparing the boundingClientRect.y properties of multiple IntersectionObserver events. Perhaps there is a better way?

Update 10.2.2021 Turns out there is a better way to handle this rather than the array method. Check out the comment from Jakub and then take a look at the example using that method. Although I’ll be using the improved method in future, I’ve left the remainder of the post as it was.

In short, my problem was that after observing something on the page I wanted to visually introduce a new element. However, I wanted the new element to stay visible, even if the observed element was no longer visible but the user was still ‘past’ the trigger element. If the user scrolled back up past the element I wanted to remove the element from view.

Anyway, that probably sounds more complicated than it needs to be. Take a look at the reduction here: https://codepen.io/benfrain/pen/GzXwgr

See the Pen
Determine IntersectionObserver direction
by Ben Frain (@benfrain)
on CodePen.

In terms of code, we create a new empty array in the global scope (or at least outside of where the function is fired). Then on each event the array gets populated by the boundingClientRect.y value of that event.

We then create a new sliced copy of that array with just the last two items of the main array and compare the last and next to last entries of the array. If the next to last was greater than the last entry we know we are heading down.

With this I was able to write a function to accommodate my needs. I’ve included all the IntersectionObserver stuff here for the sake of completeness:

var triggerElementPositions = new Array();
var triggerElem = document.querySelector(".your-TriggerElement");
var options = {
    root: null,
    rootMargin: "0px",
    threshold: [0, 0.5, 0.75, 1],
};
var observeFooter = new IntersectionObserver(showTutorialFooter, options);
observeFooter.observe(triggerElem);

function showTutorialFooter(e) {
    triggerElementPositions.push(e[0].boundingClientRect.y);
    let compareArray = triggerElementPositions.slice(triggerElementPositions.length - 2, triggerElementPositions.length);
    let down = compareArray[0] > compareArray[1] ? true : false;
    if (!down) {
        dashDepositors.setAttribute("data-footer-visible", "false");
    } else {
        if (e[0].intersectionRatio > 0.5) {
            dashDepositors.setAttribute("data-footer-visible", "true");
        }
    }
}

Essentially, if a user is scrolling up the page (or more accurately the value of the next to last position is less than the last), we set the footer to hide by default. If the user is scrolling down and the intersection is greater than 50%, we show the footer.