JSFiddle - React, Tailwind, and code Playground

by Leonardo Alipazaga

HTML

<script src="https://unpkg.com/@reactivex/[email protected]/dist/global/Rx.js"></script>
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
<script src="https://unpkg.com/@reactivex/[email protected]/dist/global/Rx.js"></script>
  <div id="container">
  </div>
</body>
</html>

CSS

#container {
  height: 200px;
  background-color: #ecf0f1;
  overflow-y: scroll;
}

.bold {
  font-weight: 700;
}

.text-center {
  text-align: center;
}

JavaScript

const Observable = Rx.Observable;
const container = document.getElementById('container');

const loading = document.createElement('p');
loading.classList.add('bold', 'text-center');
loading.innerText = 'Loading...';

// url
let nextUrl = 'https://pokeapi.co/api/v2/pokemon?limit=20&offset=20';

// loading pokemon first time
(function loadPokemons() {
  Observable.ajax({
    url: nextUrl,
    method: 'GET'
  })
  .catch(console.error)
  .do(res => (nextUrl = res.response.next))
    .map(res => res.response.results)
    .subscribe(pokemons => {
      toogleLoading(false);
      container.innerHTML +=
        pokemons.map(pokemon =>
                         pokemon.name).join('<br>')
  })
})();



function toogleLoading(showLoader) {
  showLoader ? container.appendChild(loading) : loading.remove();
}

function isScrollDown(beforePosition, currentPosition) {
  return beforePosition.scrollTop < currentPosition.scrollTop;
}

function setThreshold(threshold) {
  return function hasPassedThreshold(currentPosition) {
    return currentPosition.scrollTop * 100 /
      (currentPosition.scrollHeight -
       currentPosition.clientHeight) > threshold;
  }
}

// scrollTop: cuanto se movio la barra con respecto al top
// scrollHeight: altura del contenedor incluyendo la parte scrolleable
// clienttHeight: altura del contenedor sin incluir la parte scrolleable
Observable
  .fromEvent(container, 'scroll')
  .takeWhile(res => nextUrl)
  .map(e => ({
    scrollTop: e.target.scrollTop,
    scrollHeight: e.target.scrollHeight,
    clientHeight: e.target.clientHeight
  }))
  .pairwise()
  .filter(positions => isScrollDown(positions[0], positions[1]) && setThreshold(80)(positions[1]))
  .do(() => toogleLoading(true)) // show loader
  
  /* .switchMap(e => Observable.ajax({
    url: nextUrl,
    method: 'GET'
  }))*/
  .switchMap(() => Observable.combineLatest(Observable.timer(1000), Observable.ajax({
    url: nextUrl,
    method: 'GET'
  })))
  .map(combine => combine[1])
 ...