我正在尝试使用Vue和Express制作聊天应用程序。

此刻,我想使带有消息的容器在发送新消息时自动滚动到底部。我尝试通过使用scrollToEnd函数来选择div容器并将其scrollHeight分配给scrollTop来做到这一点:

scrollToEnd: function () {
    var messages = this.$el.querySelector('#messages')
    messages.scrollTop = messages.scrollHeight
}


这给出了以下错误:


  TypeError:无法读取null的属性'scrollHeight'


由于某些原因,在我在其他元素上进行测试时,使用querySelector总是返回null。

可以在下面找到该组件的完整代码。

<template>
    <div id="messages">
        <ul>
            <li v-for="msg in messages.slice().reverse()">{{ msg.message }}</li>
        </ul>
    </div>
</template>

<script>
import MessageService from '@/services/MessageService'

export default {
    name: 'messages',
    data () {
        return {
            messages: []
        }
    },
    mounted () {
        this.getMessages()

        this.$root.$on('newMessage', (msg) => {
            this.message = msg
            this.getMessages()
            this.scrollToEnd()
        })
    },
    methods: {
        async getMessages () {
            const response = await MessageService.fetchMessages()
            this.messages = response.data.messages
        },
        scrollToEnd: function () {
            var messages = this.$el.querySelector('#messages')
            messages.scrollTop = messages.scrollHeight
        }
    }
}
</script>

最佳答案

this.$el


  Vue实例正在管理的根DOM元素。


this.$el#messages div,无需从DOM中获取它。

然后,您可以简单地使用this.$el.lastElementChild.offsetTop获取最后一条消息并滚动到其顶部,因此,如果消息很长,则不会滚动到其起点。

在这里,我对模板进行了一些简化,以使其更明确。

<template>
    <ul id="messages">
        <li v-for="msg in messages.slice().reverse()">{{ msg.message }}</li>
    </ul>
</template>

<script>
export default {
    name: 'messages',
    data() {
        return { messages: [] };
    },
    mounted() {
        this.getMessages();
    },
    updated() {
        // whenever data changes and the component re-renders, this is called.
        this.$nextTick(() => this.scrollToEnd());
    },
    methods: {
        async getMessages () {
            // ...snip...
        },
        scrollToEnd: function () {
            // scroll to the start of the last message
            this.$el.scrollTop = this.$el.lastElementChild.offsetTop;
        }
    }
}
</script>


如果您确实想保留<div>容器,则可以使用ref

<template>
    <div id="messages">
        <ul ref="list">
            <li v-for="msg in messages.slice().reverse()">{{ msg.message }}</li>
        </ul>
    </div>
</template>


然后,在组件中,可以使用this.$refs.list引用它。


  ref用于注册对元素或子元素的引用
  零件。该参考将在父项下注册
  组件的$refs对象。如果用于纯DOM元素,则
  引用将是该元素;如果用于子组件,则
  引用将是组件实例。


尽管Vue示例经常使用本机DOM API来解决问题,但是在这种情况下使用ref会更容易。

10-07 19:33
查看更多