添加新元素时如何滚动到 div 底部

joe*_*now 2 javascript scroll vue.js vue-component

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

目前,我想让包含消息的容器在发送新消息时自动滚动到底部。scrollToEnd我尝试使用一个选择 div 容器并将其分配scrollHeight给以下函数来做到这一点scrollTop

scrollToEnd: function () {
    var messages = this.$el.querySelector('#messages')
    messages.scrollTop = messages.scrollHeight
}
Run Code Online (Sandbox Code Playgroud)

这会产生以下错误:

类型错误:无法读取 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>
Run Code Online (Sandbox Code Playgroud)

Emi*_*ron 6

this.$el

\n
\n

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

\n
\n

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

\n

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

\n

在这里,我对模板进行了一些简化,以使其开门见山。

\n
<template>\n    <ul id="messages">\n        <li v-for="msg in messages.slice().reverse()">{{ msg.message }}</li>\n    </ul>\n</template>\n\n<script>\nexport default {\n    name: \'messages\',\n    data() {\n        return { messages: [] };\n    },\n    mounted() {\n        this.getMessages();\n    },\n    updated() {\n        // whenever data changes and the component re-renders, this is called.\n        this.$nextTick(() => this.scrollToEnd());\n    },\n    methods: {\n        async getMessages () {\n            // ...snip...\n        },\n        scrollToEnd: function () {\n            // scroll to the start of the last message\n            this.$el.scrollTop = this.$el.lastElementChild.offsetTop;\n        }\n    }\n}\n</script>\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
<template>\n    <div id="messages">\n        <ul ref="list">\n            <li v-for="msg in messages.slice().reverse()">{{ msg.message }}</li>\n        </ul>\n    </div>\n</template>\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
\n

ref用于注册对元素或子组件的引用。\n 该引用将注册在父组件xe2x80x99s$refs对象下。如果在普通 DOM 元素上使用,\n引用将是该元素;如果在子组件上使用,\n引用将是组件实例。

\n
\n

虽然 Vue 示例通常使用本机 DOM API 来解决问题,但ref在本例中使用要容易得多。

\n