背景
在项目开发中,实现用户友好的输入交互是提升用户体验的关键之一。例如,在消息会话页面中,为了确保用户在发送新消息后页面能自动滚动到最底部,从而始终保持最新消息的可见性,需要实现自动滚动功能。这不仅提升了用户体验,还使得用户能够实时关注对话的最新进展。
实现思路
- 监听消息列表的变化
- 在消息变化后自动滚动到最底部
示例代码
组件结构如下
<template>
<div>
<el-dialog width="68%" :visible.sync="isShow" :before-close="close" class="kefu" :show-close="false"
:close-on-click-modal="false">
<div class="kefu-con">
<i class="el-icon-close close" @click="close" />
<div class="header">
<img :src="kefuImg" alt="">
<div class="title">小奶龙智能问答助手</div>
</div>
<div class="container" ref="container">
<div class="content" ref="content">
<div v-for="item in messageForm">
<div class="reply-container" v-if="item.type === 'reply'">
<div class="reply-content">
<div class="img-con">
<img :src="kefuImg" alt="">
</div>
<div class="reply" @click="handleProxyClick">
<div v-if="item.isXml" v-html="item.content"></div>
<p v-else>
{{ item.content }}
</p>
</div>
</div>
</div>
<div class="qs-container" v-if="item.type === 'qs'">
<div class="qs-content">
<div class="qs">
<p>
{{ item.content }}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="footer">
<img :src="kefuImg" alt="">
<div class="question-con">
<el-input class="ipt" v-model="question" placeholder="请输入想咨询的问题"></el-input>
<el-button class="btn" type="warning" round @click="send">发送</el-button>
</div>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import { getAnswer } from '@/api/checkin.js';
export default {
data() {
return {
isShow: false,
kefuImg: require("@/assets/images/headshot.png"),
question: '',
messageForm: [{
content: "<p>您好, 我是您的小奶龙,你的智能助手。 你可以问我编码相关的问题,也可以一起更高效、更高质量地完成编码工作。比如 “<span class='quick' style='color: #5094d5;cursor: pointer;' @click='quick'>动态路由实现</span>”, “<span class='quick' style='color: #5094d5;cursor: pointer;' @click='quick'>移动端适配</span>” 等等一些问题。</p>",
type: "reply", // reply 回答 qs 问题
isXml: true,
},
{
content: '动态路由实现',
type: "qs", // reply 回答 qs 问题
isXml: false,
}
]
}
},
methods: {
handleProxyClick(event) {
// 获取触发事件的目标元素 event 事件对象
const target = event.target;
// 判断目标元素是否包含指定类名
if (target.classList.contains('quick')) {
// 传递目标元素的文本内容
this.quick(target.outerText);
}
},
quick(text) {
console.log('quick方法触发了::: ');
this.question = text;
// 发送
this.send();
},
show() {
this.isShow = true;
},
close() {
this.isShow = false;
},
send() {
let NewQuestion = this.question.trim();
if (NewQuestion === '') {
return;
}
this.messageForm.push({
content: NewQuestion,
type: 'qs',
isXml: false,
});
setTimeout(() => {
this.question = '';
})
},
},
}
</script>
发送消息不滚动到最底部
解决方法
获取对话框内容 DOM 元素,将滚动容器的滚动条位置设置为其最大高度,从而使滚动容器自动滚动到最底部
container.scrollTop
-
获取当前滚动容器的垂直滚动距离。
-
设置 scrollTop 的值可以改变滚动容器的滚动位置。
-
如果 scrollTop 设置为 0,则滚动到顶部;如果设置为某个较大的值,则滚动到对应的位置。
container.scrollHeight
-
获取滚动容器的总高度(包括不可见部分)。
-
当
scrollTop
设置为scrollHeight
时,滚动容器会滚动到最底部。
// 获取内容容器
const content = this.$refs.content;
this.$refs.container.scrollTo(0, content.scrollHeight);
效果如下
当异步接口返回时,插入的内容不会滚动到最底部,我们需要使用 this.$nextTick()
函数,该函数会等待 DOM 更新完成后才调用
export default {
methods: {
send() {
let NewQuestion = this.question.trim();
if (NewQuestion === '') {
return;
}
this.messageForm.push({
content: NewQuestion,
type: 'qs',
isXml: false,
});
setTimeout(() => {
// 模拟异步请求
this.messageForm.push({
content: '11111',
type: 'reply',
isXml: false,
});
this.question = '';
this.$nextTick(() => {
// 滚动到底部
this.scrollToBottom();
})
})
},
scrollToBottom() {
const content = this.$refs.content;
this.$refs.container.scrollTo(0, content.scrollHeight);
}
}
}
效果如下
总结
在设计实现消息会话页面时,为确保每次发送新消息后,页面自动滚动至底部,关键在于利用 Vue 的响应式特性结合 DOM 操作技巧。具体策略为:当消息列表数据更新后,利用 this.$nextTick()
确保 DOM 已完成更新,随后设置容器的 scrollTop
属性等于 scrollHeight
,从而使页面平滑地滚动到消息区域的最底部,以此提升用户体验,保证用户始终能便捷地查看最新的聊天内容。