I'm trying to toggle the class when Intersection Observer passes a certain element. When it passes from the top, I'd like to add class and then to remove it when scroll direction is up.
It works, but unfortunately not each time and not in correct place. How to add / remove the class when the element is just barely visible at the bottom of the screen?
const options = {
root: null,
// rootMargin: '50px',
threshold: 0.1
}
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
// up
if (entry.boundingClientRect.top < 0) {
document.querySelector('button').classList.remove('hidden');
} else {
//down
document.querySelector('button').classList.add('hidden')
}
}
},
options
);
const arr = document.querySelectorAll('h2')
arr.forEach(i => {
observer.observe(i)
})
h2 {
margin-top: 700px;
margin-bottom: 700px;
}
button {
position: fixed;
top: 0;
left:0;
}
button.hidden {
background: red !important;
}
<h2>
ELEMENT
</h2>
<button>
FIXED BUTTON
</button>
I'm trying to toggle the class when Intersection Observer passes a certain element. When it passes from the top, I'd like to add class and then to remove it when scroll direction is up.
It works, but unfortunately not each time and not in correct place. How to add / remove the class when the element is just barely visible at the bottom of the screen?
const options = {
root: null,
// rootMargin: '50px',
threshold: 0.1
}
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
// up
if (entry.boundingClientRect.top < 0) {
document.querySelector('button').classList.remove('hidden');
} else {
//down
document.querySelector('button').classList.add('hidden')
}
}
},
options
);
const arr = document.querySelectorAll('h2')
arr.forEach(i => {
observer.observe(i)
})
h2 {
margin-top: 700px;
margin-bottom: 700px;
}
button {
position: fixed;
top: 0;
left:0;
}
button.hidden {
background: red !important;
}
<h2>
ELEMENT
</h2>
<button>
FIXED BUTTON
</button>
Share
Improve this question
asked Feb 1 at 21:00
RunnickRunnick
7154 gold badges16 silver badges36 bronze badges
1 Answer
Reset to default 1The following example was adapted from this article. There is a bug (see comment) concerning .rootBounds
so it replaced by a simple constant of 100. The class changes are applied to the entry.target
, h2
(the scrolled element being observed) and the fixed element h1
.
Details are commented in example.
const h1 = document.querySelector("h1");
const h2 = document.querySelector("h2");
/**
* Having a rootMargin would mean the target element would have
* to clear that length before it was considered intersecting
* so it was changed from 50px to 0px
*/
const options = {
root: null,
rootMargin: "0px",
threshold: 0
};
/**
* Callback function compares the target element top position
* to its bottom position in order to determine the direction
* the target element is scrolling (up or down).
* @param {object} entries - IntersectionObserverEntry
* @param {object} observer - IntersectionObserver instance
*/
const callback = (entries, observer) => {
entries.forEach(entry => {
/**
* IntersectionObserverEntry Properties
* - .intersectionRect is the clientBoundingRect of the part
* of the target that is intersecting.
* - .top is the y position of .intersectionRect relative to
* its top border and viewport.
* - .bottom is like .top concerning the bottom border.
* - .intersectionRatio is the portion of the target that's
* intersecting.
* - .target is the element being observed.
* - .isIntersecting a boolean when true is when .target is
* at or past its threshold and within the intersection
* observer root.
*/
const fromTop = entry.intersectionRect.top;
const fromBottom = 100 - entry.intersectionRect.bottom;
if (entry.intersectionRatio < 0.1 ||
fromTop > fromBottom &&
entry.isIntersecting === true) {
h1.classList.remove("red");
entry.target.classList.remove("red");
}
if (fromBottom > fromTop && entry.isIntersecting === true) {
h1.classList.add("red");
entry.target.classList.add("red");
}
});
};
/**
* Instintate IntersectionObserver pass callback function and
* options object.
*/
let observer = new IntersectionObserver(callback, options);
// Call .observe() method and pass the target element
observer.observe(h2);
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 350vh;
}
header {
position: fixed;
top: 0;
left: 0
}
.red {
color: red;
}
<header>
<h1>Header 1</h1>
</header>
<h2>Header 2</h2>