Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check for scrollTo to finish

I have an element that is scrollable. I also have a function that scrolls to a specific position. I would like to call a function when the scrollTo is finished.

Plunkr example

var x = document.querySelector('.container');
$scope.scrollTo = function() {
  x.scrollTo({
    top: 300 ,
    behavior: 'smooth'
  });
};

 // do something when scrollTo is finished
like image 637
Peter Boomsma Avatar asked Aug 20 '18 08:08

Peter Boomsma


4 Answers

By checking the position of the element I am scrolling to and comparing that to the current scroll position of the container you can see when the scrolling action is finished.

function isScrollToFinished() {
        const checkIfScrollToIsFinished = setInterval(() => {
            if (positionOfItem === scrollContainer.scrollTop) {
                // do something
                clearInterval(checkIfScrollToIsFinished);
            }
        }, 25);
}

The interval checks if the position of the scroll container is equal to the position of the element I'm scrolling to. Then do a action and clear the interval.

like image 136
Peter Boomsma Avatar answered Oct 07 '22 17:10

Peter Boomsma


I suggest checking that the scroll movement is gone.

Less parameter, no risk, and works for extremes positions.

let position = null
const checkIfScrollIsStatic = setInterval(() => {
  if (position === window.scrollY) {
    clearInterval(checkIfScrollIsStatic)
    // do something
  }
  position = window.scrollY
}, 50)

like image 41
Jonas Avatar answered Oct 07 '22 16:10

Jonas


I modified @DoiDor's answer to ensure that the position is rounded and include a timeout fallback just in case. Otherwise the position may never be reached exactly, and the promise would never resolve.

async function scrollToPosition(container, position) {
  position = Math.round(position);

  if (container.scrollTop === position) {
    return;
  }

  let resolveFn;
  let scrollListener;
  let timeoutId;

  const promise = new Promise(resolve => {
    resolveFn = resolve;
  });

  const finished = () => {
    container.removeEventListener('scroll', scrollListener);
    resolveFn();
  };

  scrollListener = () => {
    clearTimeout(timeoutId);

    // scroll is finished when either the position has been reached, or 100ms have elapsed since the last scroll event
    if (container.scrollTop === position) {
      finished();
    } else {
      timeoutId = setTimeout(finished, 100);
    }
  };

  container.addEventListener('scroll', scrollListener);

  container.scrollTo({
    top: position,
    behavior: 'smooth',
  });

  return promise;
}
like image 2
MK10 Avatar answered Oct 07 '22 17:10

MK10


I'm using a solution similar to this:

function scrollElementTo(el, position) {
  return new Promise((resolve) => {
    const scrollListener = (evt) => {
      if (typeof evt === 'undefined') {
        return;
      }

      const target = evt.currentTarget;

      if (target.scrollTop === position) {
        target.removeEventListener('scroll', scrollListener);
        resolve();
      }
    };

    el.addEventListener('scroll', scrollListener);

    // scroll to desired position (NB: this implementation works for
    // vertical scroll, but can easily be adjusted to horizontal 
    // scroll as well)
    el.scrollTo(0, position);
  });
}

const scrollContainer = document.querySelector('#theScrollContainer');

// desired y coords for scroll
const yDesiredScroll = 234;

scrollElementTo(scrollContainer, yDesiredScroll).then(() => {
    // do something here
});

like image 1
DoiDor Avatar answered Oct 07 '22 16:10

DoiDor