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])
...