- 移动优先设计:专为手机屏幕优化,使用响应式布局。
- 现代化UI:采用类似主流App(如微信、WhatsApp)的设计风格,简洁美观。
- 功能完整:包含消息发送、接收、时间戳、输入框、表情按钮、模拟“正在输入”状态等。
- 代码清晰:HTML、CSS(使用Tailwind CSS)和JavaScript分离,易于理解和修改。
- 可扩展性强:你可以轻松地添加更多功能,如文件发送、语音消息、视频通话等。
最终效果预览
这是一个静态图片,展示了模板在不同状态下的样子,实际代码是可交互的。

(图片来源网络,侵删)
第一步:准备工作
我们将使用 Tailwind CSS 来快速构建现代化的UI,你不需要下载任何东西,只需在HTML文件中引入其CDN链接即可。
第二步:完整代码
将以下所有代码复制到一个 .html 文件中,然后用浏览器打开即可看到效果,建议在手机浏览器或浏览器的“设备模拟器”模式下查看,体验更佳。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">手机Web聊天</title>
<!-- Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Font Awesome for Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* 自定义滚动条样式 */
.custom-scrollbar::-webkit-scrollbar {
width: 4px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: #f1f1f1;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #888;
border-radius: 2px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: #555;
}
/* 消息气泡动画 */
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.message-bubble {
animation: slideIn 0.2s ease-out;
}
</style>
</head>
<body class="bg-gray-100 h-screen flex flex-col overflow-hidden">
<!-- 1. 聊天头部 -->
<header class="bg-indigo-600 text-white p-4 flex items-center justify-between shadow-md">
<div class="flex items-center">
<button class="mr-3">
<i class="fas fa-chevron-left text-xl"></i>
</button>
<img src="https://i.pravatar.cc/150?img=1" alt="Avatar" class="w-10 h-10 rounded-full mr-3">
<div>
<h1 class="font-semibold text-lg">张三</h1>
<p class="text-xs opacity-80">在线</p>
</div>
</div>
<div class="flex space-x-4">
<button>
<i class="fas fa-phone text-xl"></i>
</button>
<button>
<i class="fas fa-ellipsis-v text-xl"></i>
</button>
</div>
</header>
<!-- 2. 聊天内容区域 -->
<main id="chat-container" class="flex-grow overflow-y-auto p-4 space-y-4 custom-scrollbar bg-gray-50">
<!-- 系统消息/日期分隔符 -->
<div class="flex justify-center my-4">
<span class="bg-gray-300 text-gray-600 text-xs px-3 py-1 rounded-full">lt;/span>
</div>
<!-- 接收到的消息 -->
<div class="flex items-start">
<img src="https://i.pravatar.cc/150?img=1" alt="Avatar" class="w-8 h-8 rounded-full mr-2">
<div class="max-w-xs lg:max-w-md">
<div class="bg-white p-3 rounded-2xl rounded-tl-none shadow-sm message-bubble">
<p class="text-gray-800">你好,最近怎么样?</p>
</div>
<p class="text-xs text-gray-500 mt-1 ml-2">10:30</p>
</div>
</div>
<!-- 发送的消息 -->
<div class="flex items-start justify-end">
<div class="max-w-xs lg:max-w-md">
<div class="bg-indigo-600 text-white p-3 rounded-2xl rounded-tr-none shadow-sm message-bubble">
<p>挺好的,你呢?项目进展顺利吗?</p>
</div>
<p class="text-xs text-gray-500 mt-1 mr-2 text-right">10:32</p>
</div>
</div>
<!-- 接收到的长文本消息 -->
<div class="flex items-start">
<img src="https://i.pravatar.cc/150?img=1" alt="Avatar" class="w-8 h-8 rounded-full mr-2">
<div class="max-w-xs lg:max-w-md">
<div class="bg-white p-3 rounded-2xl rounded-tl-none shadow-sm message-bubble">
<p class="text-gray-800">还不错,已经完成了80%了,遇到了一些技术难题,不过正在解决中,你那边有什么新的想法吗?</p>
</div>
<p class="text-xs text-gray-500 mt-1 ml-2">10:35</p>
</div>
</div>
<!-- 发送的消息(带表情) -->
<div class="flex items-start justify-end">
<div class="max-w-xs lg:max-w-md">
<div class="bg-indigo-600 text-white p-3 rounded-2xl rounded-tr-none shadow-sm message-bubble">
<p>太棒了!加油!🚀</p>
</div>
<p class="text-xs text-gray-500 mt-1 mr-2 text-right">10:36</p>
</div>
</div>
<!-- 模拟“正在输入”状态 -->
<div id="typing-indicator" class="flex items-start hidden">
<img src="https://i.pravatar.cc/150?img=1" alt="Avatar" class="w-8 h-8 rounded-full mr-2">
<div class="bg-white p-3 rounded-2xl rounded-tl-none shadow-sm">
<div class="flex space-x-1">
<div class="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style="animation-delay: 0ms;"></div>
<div class="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style="animation-delay: 150ms;"></div>
<div class="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style="animation-delay: 300ms;"></div>
</div>
</div>
</div>
</main>
<!-- 3. 输入区域 -->
<footer class="bg-white border-t border-gray-200 p-3">
<div class="flex items-center">
<button class="text-gray-500 mr-3 p-2">
<i class="fas fa-plus-circle text-2xl"></i>
</button>
<div class="flex-grow relative">
<input
type="text"
id="message-input"
placeholder="输入消息..."
class="w-full bg-gray-100 rounded-full py-2 px-4 pr-10 focus:outline-none focus:ring-2 focus:ring-indigo-500"
>
<button class="absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-500">
<i class="far fa-smile text-xl"></i>
</button>
</div>
<button id="send-button" class="ml-3 bg-indigo-600 text-white rounded-full w-10 h-10 flex items-center justify-center focus:outline-none focus:ring-2 focus:ring-indigo-500">
<i class="fas fa-paper-plane"></i>
</button>
</div>
</footer>
<script>
document.addEventListener('DOMContentLoaded', () => {
const chatContainer = document.getElementById('chat-container');
const messageInput = document.getElementById('message-input');
const sendButton = document.getElementById('send-button');
const typingIndicator = document.getElementById('typing-indicator');
// 自动滚动到底部
function scrollToBottom() {
chatContainer.scrollTop = chatContainer.scrollHeight;
}
// 模拟对方正在输入
function simulateTyping() {
typingIndicator.classList.remove('hidden');
scrollToBottom();
setTimeout(() => {
typingIndicator.classList.add('hidden');
// 模拟对方回复
receiveMessage();
}, 2000); // 2秒后停止输入并发送消息
}
// 发送消息
function sendMessage() {
const message = messageInput.value.trim();
if (message) {
addMessageToChat(message, 'sent');
messageInput.value = '';
scrollToBottom();
// 模拟对方回复
setTimeout(simulateTyping, 1000);
}
}
// 接收消息
function receiveMessage() {
const replies = [
"收到!",
"明白了,谢谢!",
"这个想法不错,我们讨论一下。",
"哈哈,有道理。",
"稍后我看看,有情况再联系你。"
];
const randomReply = replies[Math.floor(Math.random() * replies.length)];
addMessageToChat(randomReply, 'received');
scrollToBottom();
}
// 将消息添加到聊天界面
function addMessageToChat(message, type) {
const messageDiv = document.createElement('div');
const now = new Date();
const timeString = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
if (type === 'sent') {
messageDiv.className = 'flex items-start justify-end';
messageDiv.innerHTML = `
<div class="max-w-xs lg:max-w-md">
<div class="bg-indigo-600 text-white p-3 rounded-2xl rounded-tr-none shadow-sm message-bubble">
<p>${message}</p>
</div>
<p class="text-xs text-gray-500 mt-1 mr-2 text-right">${timeString}</p>
</div>
`;
} else {
messageDiv.className = 'flex items-start';
messageDiv.innerHTML = `
<img src="https://i.pravatar.cc/150?img=1" alt="Avatar" class="w-8 h-8 rounded-full mr-2">
<div class="max-w-xs lg:max-w-md">
<div class="bg-white p-3 rounded-2xl rounded-tl-none shadow-sm message-bubble">
<p class="text-gray-800">${message}</p>
</div>
<p class="text-xs text-gray-500 mt-1 ml-2">${timeString}</p>
</div>
`;
}
// 在“正在输入”提示之前插入消息
const typingIndicatorIndex = Array.from(chatContainer.children).indexOf(typingIndicator);
if (typingIndicatorIndex !== -1) {
chatContainer.insertBefore(messageDiv, typingIndicator);
} else {
chatContainer.appendChild(messageDiv);
}
}
// 事件监听
sendButton.addEventListener('click', sendMessage);
messageInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendMessage();
}
});
});
</script>
</body>
</html>
第三步:代码解析
HTML 结构
<header>: 聊天顶部,包含返回按钮、头像、联系人名称和在线状态,以及通话和更多选项按钮。- `: 聊天记录显示区域。
flex-grow: 让这个区域占据所有剩余空间。overflow-y-auto: 允许内容垂直滚动。space-y-4: 为每条消息之间添加垂直间距。- 消息使用
flex布局,通过justify-start(接收) 和justify-end(发送) 来控制左右对齐。 - 消息气泡
rounded-2xl和rounded-tl-none/rounded-tr-none实现了尖角效果。
<footer>: 底部输入区域,包含加号按钮(扩展功能)、输入框、表情按钮和发送按钮。
CSS (内嵌在 <style> 标签中)
- Tailwind CSS: 通过CDN引入,提供了大量的实用类,如
bg-indigo-600,text-white,p-4,rounded-full等,极大简化了样式编写。 - 自定义滚动条:
.custom-scrollbar类定义了更美观的滚动条样式。 - 消息动画:
@keyframes slideIn和.message-bubble类让新消息出现时有一个平滑的滑入效果。 - “正在输入”动画: 使用了
animate-bounce和不同的animation-delay实现了三个小圆点的跳动效果。
JavaScript (内嵌在 <script> 标签中)
这是实现交互功能的核心部分。
scrollToBottom(): 一个辅助函数,用于在消息更新后自动将聊天窗口滚动到底部。simulateTyping(): 模拟对方正在输入,它显示“正在输入”动画,2秒后自动隐藏,并调用receiveMessage()来模拟一条自动回复。sendMessage():- 获取输入框的值。
- 检查值是否为空。
- 调用
addMessageToChat()将消息添加到界面(类型为sent)。 - 清空输入框。
- 设置一个1秒的延迟,然后调用
simulateTyping()来模拟对方回复。
receiveMessage(): 从一个预设的回复数组中随机选择一条消息,然后调用addMessageToChat()将其添加到界面(类型为received)。addMessageToChat(message, type):- 创建一个新的
div元素来包裹整条消息(包括头像、气泡和时间戳)。 - 根据消息类型(
sent或received)生成不同的HTML结构,实现左右对齐和不同的气泡颜色。 - 获取当前时间并格式化。
- 使用
innerHTML将生成的HTML插入到chat-container中,它会智能地处理“正在输入”提示的位置。
- 创建一个新的
- 事件监听:
- 为发送按钮和输入框绑定
click和keypress事件,实现点击按钮或按回车键都能发送消息。
- 为发送按钮和输入框绑定
如何扩展和修改?
- 更换头像: 修改
<img src="...">中的src属性,可以链接到任何图片URL或本地图片路径。 - 修改颜色主题: 修改
header的bg-indigo-600和发送消息气泡的bg-indigo-600为你喜欢的颜色代码(如bg-blue-500,bg-green-500)。 - 添加新功能:
- 文件发送: 在加号按钮的弹出菜单中添加一个文件输入
<input type="file">,然后通过JavaScript读取文件并显示文件名或预览图。 - 语音消息: 可以添加一个录音按钮,使用
MediaRecorder API来录制并发送语音片段。 - 更多联系人: 你可以为每个联系人创建一个聊天窗口,通过JavaScript来切换显示。
- 文件发送: 在加号按钮的弹出菜单中添加一个文件输入
- 连接后端: 目前所有消息都是模拟的,要实现真实聊天,你需要将
sendMessage和receiveMessage中的逻辑替换为使用fetch或axios等库与你的后端API进行通信(发送消息到服务器,并从服务器接收新消息)。

(图片来源网络,侵删)
