document.getElementById 在 Vue2 组件中未定义...因为误用了监视?
我正在制作一个基本的聊天组件,将来自 firebase 的消息加载到 div 中。
-
目标是当
relevantMessages
更改时自动滚动到 div 的底部,如果当前滚动位置已经接近底部。
我尝试通过向
relevantMessages
添加一个调用“scrollToBottom”方法的观察器来实现这一点。
问题是:观察器“relevantMessages”的回调错误:
"TypeError: Cannot read property 'scrollTop' of undefined"
HTML:
<template>
<div class="all">
<div class="chat-header">
...
</div>
<div class="messages-area" id="messages-area-id" ref="messagesAreaRef">
<div
v-for="(thing, index) in relevantMessages"
:key="index"
class="message-row"
>
<div class="message">
<span>{{thing.content}}</span>
<span>{{thing.timeStamp}}</span>
</div>
</div>
</div>
<div class="input-area">
...
</div>
</template>
JS:
<script>
import firebase from "firebase";
export default {
data() {
return {
relevantMessages: Object,
partnerObj: null
};
},
computed: {
...
},
props: {
...
},
mounted: function() {
this.getMessages();
this.getPartner();
},
watch: {
relevantMessages: function() {
this.scrollToBottom();
}
},
methods: {
getMessages: function() {
var that = this;
firebase
.database()
.ref("/messages/" + that.conversationUID)
.on("value", function(messages) {
var result = messages.val();
that.relevantMessages = result;
});
console.log("getting posts");
},
sendMessage: function() {
...
},
scrollToBottom: function() {
var messagesArea = this.$refs.messagesAreaRef;
// var messagesArea = document.getElementById("messages-area-id");
var currentScrollPos = messagesArea.scrollTop;
// console.log(currentScrollPos);
var totalDivHeight = messagesArea.scrollHeight;
if (currentScrollPos <= totalDivHeight - 100) {
messagesArea.scrollTop(totalDivHeight);
}
}
}
};
错误本身:
vue.runtime.esm.js?2b0e:619 [Vue warn]: Error in callback for watcher "relevantMessages": "TypeError: Cannot read property 'scrollTop' of undefined"
TypeError: Cannot read property 'scrollTop' of undefined
此时组件本身可能尚未安装。因此所有
$refs
都是
未定义
。尝试在
mounted()
中使用 watch 的替代语法(
文档
)。希望有帮助!
您的实现存在一些问题:
-
relevantMessages: Object,
Object
似乎不是relevantMessages
的有效初始值。您应该将其设置为空对象 ({
) 或空数组 ([]
)。 -
messagesArea.scrollTop(totalDivHeight);
scrollTop
不是函数,应像messagesArea.scrollTop = totalDivHeight
那样使用。 -
您用于滚动到底部的实现不正确;如果
currentScrollPos
靠近 顶部 ,它将始终小于其容器的高度。要检查它是否靠近底部,您应该有currentScrollPos >= totalDivHeight - messagesArea.clientHeight - 100
现在,即使进行了上述更改,观察程序似乎也会在 Vue 有机会实际重新渲染和更新 DOM 之前运行并执行(因此,滚动将始终落后一个节点),因此您必须在
“next tick”
之后调用
scrollToBottom
:
watch: {
relevantMessages() {
this.$nextTick(this.scrollToBottom);
}
},
我在 CodeSandbox 中创建了一个工作示例: https://codesandbox.io/embed/vue-template-tqo4k 。