[Vuejs]-Vue-chat-scroll doesn't work with a loader

3👍

Look into the source codes for v-chat-scroll, scroll to Line#=27, you will find if (pause || e[e.length - 1].addedNodes.length != 1) return;

When loader=true (v-if="loader"), you will see e.length=2 and e[e.length - 1].addedNodes.length = 0 which is not as expected.

So I used one reduce to sum the addedNodes length for each MutationRecord, then works.

Vue.config.productionTip = false

// Below codes (scrollToBottom and vChatScroll) is copied from v-chat-scroll at github:
// https://github.com/theomessin/vue-chat-scroll/blob/master/src/directives/v-chat-scroll.js
const scrollToBottom = el => {
    el.scrollTop = el.scrollHeight;
};

const vChatScroll = {
    bind: (el, binding) => {
        let timeout;
        let scrolled = false;

        el.addEventListener('scroll', e => {
            if (timeout) window.clearTimeout(timeout);
            timeout = window.setTimeout(function() {
                scrolled = el.scrollTop + el.clientHeight + 1 < el.scrollHeight;
            }, 200);
        });

        (new MutationObserver(e => {
            let config = binding.value || {};
            let pause = config.always === false && scrolled;
            if (pause 
            || 
            e.reduce((pre, cur) => { // sum the length of each addedNodes
              return pre+cur.addedNodes.length
            }, 0) < 1 // if sum(addedNodes.length) === 0, do nothing.
            ) return;
            scrollToBottom(el);
        })).observe(el, {childList: true, subtree: true});
    },
    inserted: scrollToBottom
};

Vue.directive('chat-scroll', vChatScroll)

const str = [
	"Lorem ipsum dolor sit amet, consectetur adipisicing elit, similique sequi perspiciatis praesentium iure debitis explicabo animi reiciendis!",
	"Error ipsa eaque officia tempore optio laborum porro illo, veritatis atque pariatur, vero voluptatem quos",
  "At doloremque eveniet labore, eligendi dicta beatae earum aperiam et, recusandae perspiciatis perferendis corporis dolorum quidem dolores esse labore."
]

new Vue({
  el: "#app",
  data: {
    messages: [],
    loader: false
  },
  methods: {
    addMessage() {
      this.loader = true

      setTimeout(() => {
        this.loader = false
        let randMsg = str[Math.floor(Math.random()*str.length)]
        this.messages.push(randMsg)
      }, 2000)
    }
  }
})
body {
  background: #20262E;
  padding: 20px;
  font-family: Montserrat;
}

#app {
  background: #D3F3F1;
  position: relative;
  border-radius: 4px;
  height: 300px;
  width: 350px;
}
.messages-container {
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  position: absolute;
  top: 0;
  bottom: 0;
  width: 100%;
}

.message {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
  flex-shrink: 1;
  box-sizing: border-box;
  overflow-x: hidden;
  position: relative;
  width: 100%;
}

.msg-box {
  margin: 20px;
  padding: 20px;
  background-color: #fff;
  border-radius: 8px;
}

.input-message {
  position: relative;
  bottom: 0;
  background-color: white;
  width: 100%;
  height: 50px;
  text-align: center;
  flex-shrink: 0;
}

button {
  margin-top: 10px;
  border-radius: 4px;
  background: #D3F3F1;
  padding: 8px 15px;
  border: none;
}
<script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
<div id="app">
  <div class="messages-container">
    <div class="message" v-chat-scroll>
      <div>
        <div v-for="message in messages" class="msg-box">
          {{ message }}
        </div>
        <!-- Loader don't scroll to bottom -->
        <div v-if="loader" :class="loader ? 'msg-box' : ''">
          >>super loader>>
        </div>
      </div>

    </div>

    <div class="input-message">
      <button @click="addMessage">Add message</button>
    </div>
  </div>
</div>
👤Sphinx

0👍

I know the question has already been answered. But It may be better approach,
whenever message property gets updated you select as ref to your message container where
you’ve defined overflow-y: auto; property and update like follows:

var container = this.$refs.yourRef;
container.scrollTop = container.scrollHeight;

0👍

instead of adding a div

<div v-show="loader" class="msg-box">
      >>super loader>>
    </div>

inside message div, you can add loader as background-image and remove the background-image when data loading complete.
how I fix the problem !

👤kamol

Leave a comment