Issue
What I have is a div that contains about 30 cards. What I want to achieve is: every time the user scrolls I will retrieve the cards in the current viewport and consider them as “visited”. So for example, after the first scroll, the current cards in the viewport are cards with id = 1,2,3,4 and I will mark them as visited after the first scroll. How can I achieve that with optimal performance?
<div id="search-results">
<div class="search-card" id="card-1"></div>
<div class="search-card" id="card-2"></div>
<!-- 30 elements with class = "search-card" -->
</div>
I tried using the Intersection Observer API as suggested, but I think I’m doing something wrong? it doesn’t observe… It’s only triggered on page load. Should I put on scrolling event or something?
var options = {
root: document.querySelector('#search-results'),
rootMargin: '0px',
threshold: 1.0
};
var callback = function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
console.log(entry.target)
}
else {
console.log('not intersecting')
}
});
};
const observer = new IntersectionObserver(callback, options);
var targets = document.querySelectorAll('.search-card');
targets.forEach(function(target) {
observer.observe(target)
});
Solution
Set the root
property to null
. This means that the root of the document (<html>
) will be used to check if the element is visible in the viewport of the root. Since your site will probably scroll in the root element it will check if the observed elements are visible in relation to the root, which overflows outside of the screen. So it checks if the elements are within those overflowing bounds.
When using the search container, which does not have an overflow, it will check if the element is visible in context of the #search-results
container, which they always are, because no element is ever outside of the constraints of the container.
Also use an array with numbers (either integers or floating points) for the threshold
property. 0
indicates that only a single pixel of the observed element has to be in view, while 1
means that 100% of the element has to be in view, to trigger the callback function.
Check out the example below.
var options = {
root: null,
rootMargin: '0px',
threshold: [0]
};
var callback = function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
entry.target.classList.add('is-seen');
observer.unobserve(entry.target);
}
});
};
const observer = new IntersectionObserver(callback, options);
var targets = document.querySelectorAll('.search-card');
targets.forEach(function(target) {
observer.observe(target)
});
#search-results {
background: #f7f7f7;
padding: 15px;
}
.search-card {
display: block;
height: 200px;
width: 100%;
background: #ffffff;
border: 1px solid #d0d0d0;
border-radius: 5px;
margin-bottom: 15px;
transition: background-color 150ms 500ms ease-in-out;
}
.search-card.is-seen {
background: #d0d0d0;
}
<div id="search-results">
<div class="search-card" id="card-1"></div>
<div class="search-card" id="card-2"></div>
<div class="search-card" id="card-3"></div>
<div class="search-card" id="card-4"></div>
<div class="search-card" id="card-5"></div>
<div class="search-card" id="card-6"></div>
<div class="search-card" id="card-7"></div>
<div class="search-card" id="card-8"></div>
<div class="search-card" id="card-9"></div>
<div class="search-card" id="card-10"></div>
<div class="search-card" id="card-11"></div>
<div class="search-card" id="card-12"></div>
</div>
Answered By – Emiel Zuurbier
This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0