Code:
App.svelte
<script>
import { onMount } from 'svelte';
import InfiniteScroll from './InfiniteScroll.svelte';
// if the api (like in this example) just have a simple numeric pagination
let page = 1;
// but most likely, you'll have to store a token to fetch the next page
let nextUrl = '';
// store all the data here.
let data = [];
// store the new batch of data here.
let newBatch = [];
async function fetchData() {
const response = await fetch(`https://picsum.photos/v2/list?page=${page}&limit=2`);
newBatch = await response.json();
console.log(newBatch);
};
onMount(()=> {
// load first batch onMount
fetchData();
})
$: data = [
...data,
...newBatch
];
</script>
<div class="parent">
{#each data as d (d.id)}
<div class="children">
<img class="cover" src={d.download_url} alt="download"/>
</div>
{/each}
<InfiniteScroll
hasMore={newBatch.length}
threshold={100}
on:loadMore={() => {page++; fetchData()}}
/>
</div>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.parent {
max-height: 100vh;
overflow-y: auto;
scroll-snap-type: y mandatory;
margin: 0 auto;
}
.children {
position: relative;
background-color: black;
min-height: 100vh;
width: 100%;
color: white;
display: flex;
justify-content: center;
align-items: center;
font-weight: 600;
font-size: 64px;
scroll-snap-align: center;
border-bottom: 2px solid #fff;
}
.cover {
position: absolute;
left: 0;
top: 0;
background-size: cover;
max-height: 100vh;
}
</style>
InfiniteScroll.svelte
<script>
import { onMount, onDestroy, createEventDispatcher } from "svelte";
export let threshold = 0;
export let horizontal = false;
export let elementScroll;
export let hasMore = true;
const dispatch = createEventDispatcher();
let isLoadMore = false;
let component;
$: {
if (component || elementScroll) {
const element = elementScroll ? elementScroll : component.parentNode;
element.addEventListener("scroll", onScroll);
element.addEventListener("resize", onScroll);
}
}
const onScroll = e => {
const element = e.target;
const offset = horizontal
? e.target.scrollWidth - e.target.clientWidth - e.target.scrollLeft
: e.target.scrollHeight - e.target.clientHeight - e.target.scrollTop;
if (offset <= threshold) {
if (!isLoadMore && hasMore) {
dispatch("loadMore");
}
isLoadMore = true;
} else {
isLoadMore = false;
}
};
onDestroy(() => {
if (component || elementScroll) {
const element = elementScroll ? elementScroll : component.parentNode;
element.removeEventListener("scroll", null);
element.removeEventListener("resize", null);
}
});
</script>
<div bind:this={component} style="width:0px" />
When we scroll to the bottom of the list, the screen flickers and displays the previous element for a moment of time.
Encountering this trouble when trying to implement infinite scrolling, please check the repl. Thanks!
From @H.B.
InfiniteScroll should not be a component by the way. This is something that actions are made for.
Answer REPL: https://svelte.dev/repl/cb9b9e77febb496c9f9af118dd077cb6?version=3.55.0
Used svelte-inview
library and inview
action.
Code
App.svelte
<script>
import { onMount } from 'svelte';
import { inview } from 'svelte-inview';
// if the api (like in this example) just have a simple numeric pagination
let page = 0;
// but most likely, you'll have to store a token to fetch the next page
let nextUrl = '';
// store all the data here.
let data = [];
// store the new batch of data here.
let newBatch = [];
// no more data
let noMoreData = false;
async function fetchData() {
const response = await fetch(`https://picsum.photos/v2/list?page=${page}&limit=2`);
newBatch = await response.json();
console.log(newBatch);
};
onMount(()=> {
// load first batch onMount
// fetchData();
})
let visible = true;
$: data = [
...data,
...newBatch
];
</script>
<div class="parent">
{#each data as d (d.id)}
<div class="children">
<span class="center">{d.id}</span>
</div>
{/each}
<div use:inview={{}} style="height: 10px;" on:change={(e) => {
if (e.detail.inView && !noMoreData) {
page++;
fetchData();
}
}}></div>
</div>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.parent {
max-height: 100vh;
overflow-y: scroll;
scroll-snap-type: y mandatory;
margin: 0 auto;
}
.children {
position: relative;
background-color: black;
min-height: 100vh;
width: 100%;
color: white;
display: flex;
justify-content: center;
align-items: center;
font-weight: 600;
font-size: 64px;
scroll-snap-align: center;
border-bottom: 2px solid #fff;
}
.cover {
position: absolute;
left: 0;
top: 0;
background-size: cover;
max-height: 100vh;
}
</style>