I am learning how to build chat apps in Vue 3. One thing I am confused about is why the app example I made does not automatically scroll to the bottom of the screen when a new message appears (a common experience in chat apps).
Anyone know why when a new message is loaded, the browser does not automatically scroll the user down to the bottom of the screen despite my scrollIntoView method? It "works" when I manually click the "Scroll" button at the top of the page, but for some reason the Watcher is not able to do it.
Here's my code: Vue SFC playground
<template>
<section>
<button @click="scrollToElement()">Scroll</button>
<ul ref="chatContainer">
<li v-for="message in messages" :key="message.id">
{{ message.title }}
</li>
</ul>
</section>
</template>
<script setup>
import { onMounted, ref, watch } from 'vue'
const chatContainer = ref(null)
const messages = ref([])
const fetchChatMessages = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts')
const messagesArr = await response.json()
messages.value = messagesArr
}
const addNewMessage = () => {
// Simulate a new message and add it to the chat
const newMessage = {
userId: 1,
id: messages.value.length + 1,
title: 'New Message',
body: 'This is a new chat message.'
}
messages.value.push(newMessage)
}
const scrollToElement = () => {
const el = chatContainer.value
el.scrollIntoView({ block: 'end', behavior: 'smooth' })
}
watch(messages, () => {
if (chatContainer.value) {
scrollToElement()
}
}, {
immediate: true
})
onMounted(async () => {
await fetchChatMessages()
setInterval(() => {
addNewMessage()
}, 1000)
})
</script>
messagesis a ref and unfortunately a ref doesn't deep watch objects. That's actually misleading, I even created an issue: https://github.com/vuejs/docs/issues/2454#issuecomment-1783779632But you don't need to deep watch your messages since it's expensive, just use a callback watch:
A more detailed explanation of the problem is here:
https://stackoverflow.com/a/76786671/14098260