1👍
✅
Save the remaining distance to scroll to the bottom of the container. This is because if we want the scroll to be perceived as unchanged and we add messages above, then this bottom scroll distance should remain unchanged:
const { scrollTop, scrollHeight } = this.$refs.messages;
const scrollBottom = scrollHeight - scrollTop;
Then, use this.$nextTick
to wait for the DOM update from prepending the messages to change the scroll "bottom" position back to what we saved before:
this.$nextTick(() => {
this.$refs.messages.scrollTop = this.$refs.messages.scrollHeight - scrollBottom;
});
Live example:
const { createApp } = Vue;
const app = createApp({
data() {
return {
loadingGetMoreMessages: false,
chat: {
messages: Array(10)
.fill()
.map((_, i) => ({
message_id: i,
yours: false,
seen: false,
time: `09:${String(i).padStart(2, '0')}`,
message: `Foo ${i}`,
})),
},
};
},
mounted() {
this.$refs.messages.scrollTop = 91;
//add event listener to messages container
this.$refs.messages.addEventListener("scroll", () => {
if (this.$refs.messages.scrollTop < 90) {
this.loadingGetMoreMessages = true;
// get older messages and add them in all messages array
this.getMoreMessages()
}
})
},
methods: {
getMoreMessages() {
const { scrollTop, scrollHeight } = this.$refs.messages;
const scrollBottom = scrollHeight - scrollTop;
const { length } = this.chat.messages;
this.chat.messages = Array(5)
.fill()
.map((_, i) => ({
message_id: i + length,
yours: false,
seen: false,
time: `09:${String(i).padStart(2, '0')}`,
message: `Bar ${i}`,
}))
.concat(this.chat.messages);
this.$nextTick(() => {
this.$refs.messages.scrollTop = this.$refs.messages.scrollHeight - scrollBottom;
});
}
},
});
app.mount('#app');
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.3.4/vue.global.min.js" integrity="sha512-Wbf9QOX8TxnLykSrNGmAc5mDntbpyXjOw9zgnKql3DgQ7Iyr5TCSPWpvpwDuo+jikYoSNMD9tRRH854VfPpL9A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css" integrity="sha512-t4GWSVZO1eC8BM339Xd7Uphw5s17a86tIZIj8qRxhnKub6WoyhnrxeCIMeAqBPgdZGlCcG2PrZjMc+Wr78+5Xg==" crossorigin="anonymous" referrerpolicy="no-referrer"
/>
<div id="app" style="height: 200px">
<div class="d-flex flex-column gap-2 h-100 w-100 pt-3" style="overflow-y: auto; overflow-x: hidden" ref="messages">
<div v-for="(message, index) in chat.messages" :key="index">
<div :class="{ 'message-container': true, 'flex-row-reverse': !message.yours }" :ref="'m-' + message.message_id">
<div class="d-flex gap-2" :style="{ 'padding-right': !message.yours ? '20px' : '' }">
<div :class="{ 'your-message': message.yours, 'its-message': !message.yours }">
{{ message.message }}
<div class="d-flex align-items-center gap-2">
<i class="text-muted fi fi-br-check d-flex" v-if="!message.seen && message.yours"></i>
<i class="text-muted fi fi-br-check-double d-flex" v-if="message.seen && message.yours"></i>
<p class="text-muted mt-1" style="font-size: .8rem"> {{ message.time }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Source:stackexchange.com