0

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">&nbsp;</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 target document
  • Validate the body does not have height: 100%, conflicting with the scroll event behavior
  • Confirm that overflow is not being manipulated on the Y coordinate
Barmar
  • 741,623
  • 53
  • 500
  • 612
Joey O
  • 315
  • 2
  • 17
  • 2
    @blex This seems to be the opposite problem. He's using `document.documentElement`, which is the solution in that question. – Barmar Jan 02 '19 at 21:15
  • @Barmar, the point is that different browsers have different implementations. The solution is not to use one or the other, but both, for compatibility https://codepen.io/anon/pen/qLpJpO _(tested in Chrome, Firefox and Edge)_ – blex Jan 02 '19 at 21:21
  • @blex – your demonstration appears to have resolved my issue! thanks a ton! Do you have any other recommendations for my implementation? I'm not sure how to mark your comment as the acceptable answer though? – Joey O Jan 02 '19 at 21:43
  • Cool! I don't have any other recommendation, except for this: `$( document.getElementsByClassName('data-points__stat') )` which could just be `$('.data-points__stat')`. You cannot mark a comment as an accepted answer, I did not post it as an answer because I think this question is a duplicate. I could be wrong, as @Barmar pointed out :) – blex Jan 02 '19 at 21:51

0 Answers0