Reputation: 2465
In the code example below, there is a button to scroll to the bottom of the page, passing over six observed divs.
isIntersecting
fires for each element as they pass through the viewport. Is it possible to throttle or add a delay to the trigger to prevent the intersection event for elements that are only briefly in the viewport?
Ideally, in the example below, the interaction counter would be 2
after clicking the scroll down button - one detection for the first element and one for the last element, ignoring those in between
const blocks = document.querySelectorAll('.block');
const counter = document.querySelector('.counter');
const observer = new IntersectionObserver(callback, {
threshold: 0.5
});
let count = 0;
blocks.forEach(block => observer.observe(block));
function scrollDown(y = document.body.clientHeight) {
window.scrollTo({
top: y,
behavior: 'smooth'
});
}
function callback(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
count++;
counter.innerText = `Intersections: ${count}`;
entry.target.style.backgroundColor = 'lightblue';
} else {
entry.target.style.backgroundColor = 'lightpink';
}
});
};
.block {
width: 50vw;
height: 80vh;
margin: 20px auto;
}
.counter {
position: fixed;
top: 50px;
left: 20px;
}
<p class="counter"></p>
<button onclick="scrollDown();">Scroll To Bottom</button>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<button onclick="scrollDown(0);">Scroll To Top</button>
Upvotes: 3
Views: 5367
Reputation: 31992
Here's one approach:
everytime an intersecting element is found, increment count
and perform the necessary handling in the setTimeout
callback set to run after 500
milliseconds (this number can be changed in accordance to the scroll speed)
store the number returned by setTimeout
in a variable
when setting the setTimeout
, check whether the variable contains a value. If so, use clearTimeout
to remove that setTimeout
, so whatever code you scheduled to execute won't run.
once you've stopped scrolling, the code in the callback of the setTimeout
will execute, and only the last element scrolled to will be handled (since previous setTimeout
s to handle the other elements were cleared).
const blocks = document.querySelectorAll('.block');
const counter = document.querySelector('.counter');
const observer = new IntersectionObserver(callback);
let count = 0;
blocks.forEach(block => observer.observe(block));
function scrollDown(y = document.body.clientHeight) {
window.scrollTo({
top: y,
behavior: 'smooth'
});
}
var lastTimeout;
function callback(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
if (lastTimeout) clearTimeout(lastTimeout)
lastTimeout = setTimeout(function() {
count++;
counter.innerText = `Intersections: ${count}`;
entry.target.style.backgroundColor = 'lightblue';
}, 500);
} else {
entry.target.style.backgroundColor = 'lightpink';
}
});
};
.block {
width: 50vw;
height: 80vh;
margin: 20px auto;
}
.counter {
position: fixed;
top: 50px;
left: 20px;
}
<p class="counter"></p>
<button onclick="scrollDown();">Scroll To Bottom</button>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<button onclick="scrollDown(0);">Scroll To Top</button>
Upvotes: 2