Quantcast
Channel: Active questions tagged javascript - Stack Overflow
Viewing all articles
Browse latest Browse all 140705

How to control inertia scrolling? Prevent multiple API calls due to inertia

$
0
0

I have to make request to an API when window.scrollTop < 20. On success, I prepend a new child component to the top of .list component.

I use throttle to prevent multiple API calls that occurs due to inertia of scroll. It works well if fetch duration is close to throttle duration and both are greater than 800 ( I am not sure why this works well only when duration is larger than 800, not necessarily but works well for large duration). It does not work well otherwise - say duration is close to 300.

Behaviour -

  1. due to inertia scrolling, window continues to scroll after successful fetch and window.scrollTop once again is less than 20 that makes another API call. or,
  2. window.scrollTop is less than 20 and scroll bar hits top.

Parameters on which this behaviour depends is:

  1. momentum of scroll
  2. height of prepended list child.
  3. throttle duration
  4. fetch duration

To end inertia of scroll, I tried overflow: none. This makes UI unresponsive for fetch duration that is not acceptable.

I maintain the position of scrollTop that was at the time of fetch request , for which I dowindow.scrollTo(0, scrollHeight - state.docScrollHeight). I record the position of scrollTop at the time of fetch request instate.docScrollHeight`.

I want to prevent multiple API calls and maintain scroll position.

Fiddle: https://jsfiddle.net/05mwb3hq/1/

This issue is best reproduced in developer tool.

var state = {
  loadingMore: false,
  docScrollHeight: 0,
  lastCall: 0,
  lastCallToFetch: 0,
}

var count = 0;

function createEl() {
  let el = document.createElement('div');
  el.classList.add('list-comp');
  let elNum = document.createElement('span');
  elNum.textContent = count;
  elNum.classList.add('elNum');
  el.appendChild(elNum);
  const listEl = document.querySelector('#list');
  listEl.prepend(el);
  count++;
}

function mockApi(lagDuration) {
  return new Promise(resolve => {
    setTimeout(resolve, lagDuration)
  });
}

function throttle(fetchCb, throttleDuration) {
  let previousCall = state.lastCall;
  state.lastCall = Date.now();

  if (previousCall === undefined || (state.lastCall - previousCall) > throttleDuration) {
    state.lastCallToFetch = Date.now();
    state.loadingMore = true;
    document.querySelector('.loader').classList.remove('d-none');
    fetchCb(300)
      .then(() => {
        createEl();
        state.loadingMore = false;
      })
      .then(() => {
        const {
          scrollHeight
        } = document.documentElement;
        if (!state.loadingMore) {
          window.scrollTo(0, scrollHeight - state.docScrollHeight);
          document.querySelector('.loader').classList.add('d-none');
        }
      });
  }
}

var onScrollList = function on_scroll_list() {
  if (window.scrollY < 20) {
    if (!state.loadingMore) {
      state.docScrollHeight = document.documentElement.scrollHeight;
      state.loagingMore = true;
      throttle(mockApi, 800);
    }
  }
}

window.addEventListener('scroll', onScrollList);

window.onload = function() {
  window.scrollTo(0, document.body.clientHeight);
}
* {
  box-sizing: border-box;
}

.d-none {
  display: none;
}

#main-scrollable-comp {
  overflow-y: scroll;
  background-color: grey;
}

#default-comp {
  background-color: red;
  height: 800px;
}

.list-comp {
  height: 200px;
  width: 100%;
  background-color: purple;
  border: 2px solid yellow;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

.elNum {
  color: green;
  font-size: 54px;
}

.loader-container {
  height: 100px;
  background-color: white;
  display: flex;
  align-items: center;
  justify-content: center;
}

.loader {
  border: 4px solid #f3f3f3;
  border-top: 4px solid #3498db;
  border-radius: 50%;
  width: 36px;
  height: 36px;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Smooth Scroll</title>
  <link href="styles.css" rel="stylesheet">
</head>

<body>
  <div id="main-scrollable-comp">
    <div class='loader-container'>
      <div class='loader d-none'></div>
    </div>
    <div id='list'></div>
    <div id="default-comp"></div>
  </div>
</body>

</html>

Viewing all articles
Browse latest Browse all 140705

Latest Images

Trending Articles



Latest Images

<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>