I've dug around a bit on StackOverflow hoping to find an answer to a bit of a but I'm experiencing. I'm attempting to fire an event based on scroll and if an element is within view (by calculating the top of the element from the top of the DOM).
Now, I'm able to get this to work on all major browsers, but, for some reason I'm tripping over IE/Edge quirks.
I've put together a codepen to hopefully help speed up the troubleshooting process. The primary lines to focus on are 41 & 42 of the codepen JS.
const self = this;
const debug = true;
const parent = document.getElementById('js-data-count');
let scrolling = false;
let touchmoving = false;
let inView = false;
// Create a constant that should only fire ONCE
const animateOnce = (function() {
let executed = false;
return function() {
if (!executed) {
executed = true;
$(document.getElementsByClassName('data-points__stat')).each(function() {
let $this = $(this);
let countTo = $this.attr('data-count-to');
$({
countNum: $this.text()
}).animate({
countNum: countTo
}, {
duration: 1500,
easing: 'linear',
step: function() {
$this.text(Math.floor(this.countNum));
},
complete: function() {
$this.text(this.countNum);
}
});
});
}
};
})();
// If on load the area is in view but the user hasn't scrolled to it (refresh the page for example)
window.onload = function() {
if (parent) {
window.addEventListener('scroll', function() {
scrolling = true;
});
window.addEventListener('touchmove', function() {
touchmoving = true;
});
const fromTop = parent.offsetTop + 50;
const element = window.innerHeight + document.documentElement.scrollTop;
const visible = fromTop <= element;
if (visible) {
inView = true;
animateOnce();
if (debug) {
console.log("Target(s) ARE within viewport AND user hasn't scrolled")
}
}
}
};
// Set an interval to fire the desired functions on scroll but prevent excessive recalculations
setInterval(function() {
// If the parent element is present on the page
if (parent) {
const fromTop = parent.offsetTop + 50;
const element = window.innerHeight + document.documentElement.scrollTop;
const visible = fromTop <= element;
const notVisible = fromTop > element;
// If the user is actively scrolling
if (scrolling) {
scrolling = false;
// If the target area is NOT within the viewport
if (notVisible) {
if (debug) {
console.log("Target(s) ARE NOT within viewport AND user has scrolled")
}
inView = false;
return;
}
// If the target area IS within the viewport
if (visible) {
if (debug) {
console.log('Target(s) ARE within viewport AND user scrolled')
}
inView = true;
animateOnce();
}
}
}
}, 1000); // set a delay to avoid event spamming
// Change the value below to test the scroll conditions
.artificial-spacer {
background-color: #c0c0c0;
height: 95vh;
position: relative;
&:before {
content: 'Scroll down please!';
font-weight: bold;
width: 50%;
margin: auto;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
}
<section class="artificial-spacer"> </section>
<section class="data-points">
<div class="data-points__wrapper">
<h1 class="data-points__title">Nam id nibh urna. Aenean sollicitudin in felis sit amet ornare. Donec scelerisque tellus urna, ac dignissim sem varius id.</h1>
<ul class="data-points__list multiple" id="js-data-count">
<li class="data-points__item">
<a href="#random-link1" class="data-points__link" aria-label="View the Link" title="View the Link">
<div class="data-points__bubble">
<span class="data-points__stat" data-count-to="24">0</span>
<span class="data-points__type">%</span>
</div>
</a>
<p class="data-points__text">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</li>
<li class="data-points__item">
<a href="#random-link2" class="data-points__link" aria-label="View the Link" title="View the Link">
<div class="data-points__bubble">
<span class="data-points__stat" data-count-to="60">0</span>
<span class="data-points__type">%</span>
</div>
</a>
<p class="data-points__text">Quisque fringilla tellus sed ipsum euismod, a laoreet nisl blandit.</p>
</li>
<li class="data-points__item">
<a href="#random-link3" class="data-points__link" aria-label="View the Link" title="View the Link">
<div class="data-points__bubble">
<span class="data-points__stat" data-count-to="55">0</span>
<span class="data-points__type">%</span>
</div>
</a>
<p class="data-points__text">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</li>
</ul>
</div>
</section>
I've experimented with:
- Adjusting the
window
events to targetdocument
- Validate the
body
does not haveheight: 100%
, conflicting with the scroll event behavior - Confirm that
overflow
is not being manipulated on the Y coordinate