I have a list of chat messages inside a div and want to scroll to the bottom each time an element is added. I tried calling a function that selects the last item and uses scrollIntoView()
.
scrollToElement:function() { const el = this.$el.getElementsByClassName('message'); if (el) { el[el.length-1].scrollIntoView({behavior: "smooth"}); } }
The issue is that it scrolls to the top of the selected element and not to the bottom of it which is needed in order to include the entire element into view.
I expected:
Advertisement
Answer
Every time you append a new chat message to the chat container – you need to scroll the chat container to its bottom edge. You can do that with a simple assignment:
this.$refs.chatContainer.scrollTop = this.$refs.chatContainer.scrollHeight;
Please note that the scrolling must be performed inside $nextTick
to ensure that the new chat message has been added to the DOM.
My advice is to use a Vue directive on the chat container which will automatically scroll to the bottom every time a new chat message has been added:
function scrollToBottom(el) { el.scrollTop = el.scrollHeight; } // Monitors an element and scrolls to the bottom if a new child is added // (always:false = prevent scrolling if user manually scrolled up) // <div class="messages" v-chat-scroll="{always: false}"> // <div class="message" v-for="msg in messages">{{ msg }}</div> // </div> Vue.directive('chat-scroll', { bind: function(el, binding) { var timeout, scrolled = false; el.addEventListener('scroll', function(e) { if (timeout) window.clearTimeout(timeout); timeout = window.setTimeout(function() { scrolled = el.scrollTop + el.clientHeight + 1 < el.scrollHeight; }, 200); }); (new MutationObserver(function(e) { var config = binding.value || {}; var pause = config.always === false && scrolled; if (pause || e[e.length - 1].addedNodes.length != 1) return; scrollToBottom(el); })).observe(el, {childList: true}); }, inserted: scrollToBottom });