'Use IntersectionObserver To Trigger Event AFTER Element Completely Passes Threshold
I have a few IntersectionObserver's set up. observer toggles new boxes to be made as the user scrolls down the page. lastBoxObserver loads new boxes as this continuous scrolling happens.
What I would like to do is change the color of a box once it leaves the threshold set in the first observer (observer - whose threshold is set to 1). So, once box 12 enters the viewport, it passes through the observer, and once it has completely passed outside of the threshold for this observer and box 13 enters the observer, box 12's background changes from green to orange, perhaps.
Is there a way to make this happen? Maybe by adding an additional observer, or adding code to observer? Any help is greatly appreciated.
Codepen: https://codepen.io/jon424/pen/NWwReEJ
JavaScript
const boxes = document.querySelectorAll(".box");
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
entry.target.classList.toggle("show", entry.isIntersecting);
if (entry.isIntersecting) observer.unobserve(entry.target);
});
},
{
threshold: 1,
}
);
const lastBoxObserver = new IntersectionObserver((entries) => {
const lastBox = entries[0];
if (!lastBox.isIntersecting) return;
loadNewBoxes();
lastBoxObserver.unobserve(lastBox.target);
lastBoxObserver.observe(document.querySelector(".box:last-child"));
}, {});
lastBoxObserver.observe(document.querySelector(".box:last-child"));
boxes.forEach((box) => {
observer.observe(box);
});
const boxContainer = document.querySelector(".container");
function loadNewBoxes() {
for (let i = 0; i < 1000; i++) {
const box = document.createElement("div");
box.textContent = `${i + 1}`;
box.classList.add("box");
observer.observe(box);
boxContainer.appendChild(box);
}
}
HTML
<div class="container">
<div class="box">0</div>
</div>
CSS
.container {
display: flex;
flex-direction: column;
gap: 1rem;
align-items: flex-start;
}
.box {
background: green;
color: white;
font-size: 4rem;
text-align: center;
margin: auto;
height: 100px;
width: 100px;
border: 1px solid black;
border-radius: 0.25rem;
padding: 0.5rem;
transform: translateX(100px);
opacity: 0;
transition: 150ms;
}
.box.show {
transform: translateX(0);
opacity: 1;
}
.box.show.more {
background-color: orange;
}
Solution 1:[1]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<style>
.container {
display: flex;
flex-direction: column;
gap: 1rem;
align-items: flex-start;
}
.box {
background: green;
color: white;
font-size: 4rem;
text-align: center;
margin: auto;
height: 100px;
width: 100px;
border: 1px solid black;
border-radius: 0.25rem;
padding: 0.5rem;
opacity: 0;
}
.box.show {
opacity: 1;
}
.box.show.more {
background-color: orange;
}
.mytest{
border:solid 10px #000;
background-color: orange;
}
</style>
<div class="container">
<div class="box">0</div>
<div class="sentinel"></div>
</div>
<script>
/* https://stackoverflow.com/questions/70994897/use-intersectionobserver-to-trigger-event-after-element-completely-passes-thresh/71054028#71054028 */
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("show");
}
if (entry.boundingClientRect.top < entry.rootBounds.top) {
/* entry is above viewport */
entry.target.classList.add("mytest");
observer.unobserve(entry.target);
}
});
},
{
threshold: 0, /* top is 200 below top of viewport - something for the box to scroll into*/
rootMargin: "-200px 0px 0px 0px"
}
);
/* last div sentinel indicates last box */
const mysentinel = new IntersectionObserver((entries) => {
var entry = entries[0];
if (entry.isIntersecting) {
loadNewBoxes();
}
},{
threshold: 0
});
const boxContainer = document.querySelector(".container");
const sentinel = document.querySelector(".sentinel");
/* setup - create extra boxes, sentinel is last after boxes */
loadNewBoxes();
mysentinel.observe(sentinel);
function loadNewBoxes() {
for (let i = 0; i < 10; i++) {
const box = document.createElement("div");
box.textContent = `${i + 1}`;
box.classList.add("box");
box.classList.add("show");
observer.observe(box);
boxContainer.appendChild(box);
}
boxContainer.appendChild(sentinel);
}
</script>
</body>
</html>
IntersectionObserver callback is async, if you scroll fast enough then some boxes will be missed. I've simplified your code. If the box/observed target is above the root top then I add a class and unobserve it. If the box is transitioning I add a class to show the box. I use sentinel to indicate the last box and to add more boxes when the sentinel hits the viewport.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | JoePythonKing |
