Skip to content
Advertisement

Use scrollIntoView() and scroll to the bottom of the selected element

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:
enter image description here

I got:
enter image description here

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
  });
User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement