Issues with inverted/top infinite load.
Below is the code I've put together to do inverted infinite scrolling. I want the behavior to behave exactly like your demo, however, I keep running into the following issues and cannot figure out why mine is behaving different than yours.
- The initial load won't start at the bottom of the scroll element unless the
slotattribute is bound. It's weird. If I override a slot with a template alone, it starts at the top of the scroll element, which is wrong. If I override a slot with the attribute alone, it starts at the bottom of the scroll element, which it should. I prefer to override with a template, so in order to make it work I have to include the template and the attribute binding with an empty object as shown in the code below. This doesn't seem right. - The transition is not smooth. Whenever the infinite handler triggers, it automatically scrolls all the way to the top of all the new messages. This also isn't like your demo.
Do you see what I am doing wrong to cause these issues? Thanks!
Edit: This is partial code. I stripped out a bunch of other stuff that is unrelated to the infinite scroll stuff.
const template = `
<div class="flex-row messaging">
<div class="flex-grow flex-column flex-justify-end messages">
<div id="message-container" class="message-container">
<infinite-loading target="#message-container" :slots="{}" :top="true" :identifier="infiniteId" :firstload="false" @infinite="infiniteHandler">
<template #complete>
<span></span>
</template>
</infinite-loading>
<div v-for="message in messages" v-bind:key="message.id" v-bind:message="message" class="flex-row message">
... stuff ...
</div>
</div>
</div>
</div>
`
export default {
name: "Messages",
template,
setup () {
const messages = ref([]);
const cursor = ref(null);
function getMessages($state) {
var query = "";
if (cursor.value) {
query = "?cursor="+cursor.value;
};
fetchWrapper.get(`${API_BASE_URL}/messages${query}`)
.then(json => {
cursor.value = json.cursor;
// prepend messages with the older messages
messages.value.unshift(...json.messages);
if (json.next) {
$state.loaded();
} else {
$state.complete()
};
})
.catch(error => appStore.createToast(error));
};
function infiniteHandler($state) {
getMessages($state);
};
return {
messages,
infiniteId,
infiniteHandler,
};
},
};
After a lot of testing and reviewing the code for this component, I think I found out what's causing my issues.
The $state.complete method doesn't calculate the scrollTop like the $state.loaded method does, which causes the component to jump to the last bit of data on the final load. In your demo, there is an unending stream of data, so the user never gets to the last request where the complete method is called. I found this behavior because I am testing with a small amount of data. I tried to fix the problem by calling the loaded method just before complete, but the isVisible function triggers a loop of loads until the target element is filled, and if there isn't enough data to fill the target element, then it keeps trying to load data. The issues that result are dependent on the API providing the data, but it isn't a viable workaround.
Is this something that needs to be fixed, or am I missing something?
Here's my temporary fix, which proves out the issue. The first line in my infinite handler stores the target element's previous height in the local state. Just like your component does. Then, when the complete condition is met, I follow the call to the complete method with the following code. The final transition is now smooth.
nextTick(() => {
scrollContainer.value.scrollTop = scrollContainer.value.scrollHeight - prevHeight;
});
I'v tried to reproduce this issue but it works just fine for me If you can manage to provide a fully functional example, it would help a lot. You can start from this one on StackBlitz
I'v tried to reproduce this issue but it works just fine for me If you can manage to provide a fully functional example, it would help a lot. You can start from this one on StackBlitz
Your example doesn't accurately represent the problem because line 16 of App.vue will never be true in your app. The API is never ending, so it will always return 10. However, if you had a dataset of 38 items, then on the last request for data that line would be true and you would experience the issue I am reporting.
Use the following App.vue in your stackblitz to replicate the issue. Notice how the final load causes a jump to the top.
<script setup>
import { ref } from 'vue';
import InfiniteLoading from 'v3-infinite-loading';
import 'v3-infinite-loading/lib/style.css';
let comments = ref([]);
let page = 0;
const load = async ($state) => {
console.log('loading more...');
page++;
try {
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts?_limit=30&_page=' + page
);
const json = await response.json();
comments.value.unshift(...json);
if (json.length < 30) {
$state.complete();
} else {
$state.loaded();
}
} catch (error) {
$state.error();
}
};
</script>
<template>
<div class="top-results">
<InfiniteLoading @infinite="load" :top="true" target=".top-results" />
<div v-for="comment in comments" :key="comment.id" class="result">
<div>{{ comment.title }}</div>
<div>{{ comment.id }}</div>
</div>
</div>
</template>
<style>
.top-results {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
height: 500px;
width: 380px;
overflow-y: scroll;
margin: 0 auto;
border-radius: 10px;
padding: 10px;
}
.result {
font-weight: 300;
width: 80%;
padding: 20px;
text-align: center;
background: #eceef0;
border-radius: 10px;
}
</style>
Oh okay, now i see the issue. Thanks.
Oh okay, now i see the issue. Thanks.
Do you plan to fix it?
Any progress on this issue?
Any updates on the issue? i got the same problem
Same issue!!! Any update please