功能特点
- 添加留言:用户可以输入昵称和留言内容并提交。
- 实时显示:新留言会立即显示在列表的顶部。
- 时间戳:每条留言都带有精确的发布时间。
- 响应式设计:在桌面、平板和手机上都能良好显示。
- 交互反馈:提交后有成功提示,输入框有焦点状态。
- 数据持久化:使用
localStorage保存留言,刷新页面后留言不会丢失。 - 清空功能:提供了一个清空所有留言的按钮(需要二次确认)。
- 现代化UI:使用了 CSS3 的渐变、阴影、过渡动画等效果。
最终效果预览
这是一个静态的 GIF 预览,展示了添加留言、刷新页面数据保留以及清空留言的功能。

(图片来源网络,侵删)
代码实现
您只需要将以下三个代码块分别保存为 .html, .css, 和 .js 文件,并将它们放在同一个文件夹下,然后用浏览器打开 index.html 即可运行。
HTML 代码 (index.html)
这是页面的结构,包含标题、输入表单和留言列表容器。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">HTML5 & CSS3 留言板</title>
<link rel="stylesheet" href="style.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
</head>
<body>
<div class="container">
<header>
<h1>✍️ 我的留言板</h1>
<p>分享你的想法,与世界互动</p>
</header>
<main>
<!-- 留言输入表单 -->
<section class="message-form">
<form id="messageForm">
<div class="form-group">
<input type="text" id="nickname" placeholder="请输入你的昵称" required maxlength="20">
</div>
<div class="form-group">
<textarea id="content" placeholder="写下你想说的话..." required maxlength="200"></textarea>
</div>
<button type="submit" id="submitBtn">发布留言</button>
</form>
</section>
<!-- 操作提示和清空按钮 -->
<section class="actions">
<div id="successMessage" class="success-message">
留言发布成功!
</div>
<button id="clearBtn" class="clear-btn">清空所有留言</button>
</section>
<!-- 留言列表 -->
<section class="message-list" id="messageList">
<!-- 留言将通过 JavaScript 动态添加到这里 -->
<div class="message-item">
<div class="message-header">
<span class="nickname">示例用户</span>
<span class="time">2025-10-27 10:30:00</span>
</div>
<div class="message-content">
欢迎使用这个留言板!这是第一条示例留言,你可以删除它或添加你自己的留言。
</div>
</div>
</section>
</main>
</div>
<script src="script.js"></script>
</body>
</html>
CSS 代码 (style.css)
这是样式表,负责页面的所有视觉呈现,包括布局、颜色、字体和动画。
/* --- 全局样式和变量 --- */
:root {
--primary-color: #007bff;
--secondary-color: #6c757d;
--success-color: #28a745;
--danger-color: #dc3545;
--light-bg: #f8f9fa;
--white: #ffffff;
--text-color: #333;
--border-color: #dee2e6;
--shadow-sm: 0 2px 4px rgba(0,0,0,0.05);
--shadow-md: 0 4px 8px rgba(0,0,0,0.1);
--shadow-lg: 0 8px 16px rgba(0,0,0,0.1);
--border-radius: 8px;
--transition: all 0.3s ease;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Noto Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: linear-gradient(135deg, #e0f7fa 0%, #bbdefb 100%);
color: var(--text-color);
line-height: 1.6;
min-height: 100vh;
}
.container {
max-width: 800px;
margin: 2rem auto;
padding: 0 1rem;
}
/* --- 头部样式 --- */
header {
text-align: center;
margin-bottom: 2rem;
animation: fadeInDown 0.8s ease-out;
}
header h1 {
font-size: 2.5rem;
color: var(--primary-color);
margin-bottom: 0.5rem;
}
header p {
font-size: 1.1rem;
color: var(--secondary-color);
}
/* --- 主要内容区域 --- */
main {
background: var(--white);
border-radius: var(--border-radius);
box-shadow: var(--shadow-lg);
padding: 2rem;
animation: fadeIn 1s ease-out;
}
/* --- 表单样式 --- */
.message-form {
margin-bottom: 2rem;
}
.form-group {
margin-bottom: 1rem;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 12px 15px;
border: 2px solid var(--border-color);
border-radius: var(--border-radius);
font-size: 1rem;
font-family: inherit;
transition: var(--transition);
}
.form-group input:focus,
.form-group textarea:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.25);
}
.form-group textarea {
resize: vertical;
min-height: 100px;
}
#submitBtn {
display: block;
width: 100%;
padding: 12px;
background: linear-gradient(to right, var(--primary-color), #0056b3);
color: var(--white);
border: none;
border-radius: var(--border-radius);
font-size: 1.1rem;
font-weight: 500;
cursor: pointer;
transition: var(--transition);
}
#submitBtn:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
#submitBtn:active {
transform: translateY(0);
}
/* --- 操作区域样式 --- */
.actions {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
flex-wrap: wrap;
gap: 1rem;
}
.success-message {
background-color: var(--success-color);
color: var(--white);
padding: 10px 15px;
border-radius: var(--border-radius);
opacity: 0;
transform: translateY(-10px);
transition: var(--transition);
}
.success-message.show {
opacity: 1;
transform: translateY(0);
}
.clear-btn {
padding: 8px 16px;
background-color: var(--danger-color);
color: var(--white);
border: none;
border-radius: var(--border-radius);
cursor: pointer;
transition: var(--transition);
font-size: 0.9rem;
}
.clear-btn:hover {
background-color: #c82333;
transform: translateY(-1px);
}
/* --- 留言列表样式 --- */
.message-list {
max-height: 600px;
overflow-y: auto;
padding-right: 10px;
}
.message-list::-webkit-scrollbar {
width: 8px;
}
.message-list::-webkit-scrollbar-track {
background: var(--light-bg);
border-radius: 10px;
}
.message-list::-webkit-scrollbar-thumb {
background: var(--secondary-color);
border-radius: 10px;
}
.message-list::-webkit-scrollbar-thumb:hover {
background: #5a6268;
}
.message-item {
background: var(--light-bg);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 1.5rem;
margin-bottom: 1rem;
animation: slideIn 0.5s ease-out;
transition: var(--transition);
}
.message-item:hover {
box-shadow: var(--shadow-md);
transform: translateX(5px);
}
.message-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.8rem;
}
.nickname {
font-weight: 700;
color: var(--primary-color);
font-size: 1.1rem;
}
.time {
font-size: 0.85rem;
color: var(--secondary-color);
}
.message-content {
color: var(--text-color);
line-height: 1.7;
white-space: pre-wrap; /* 保留换行 */
}
/* --- 空状态样式 --- */
.empty-state {
text-align: center;
padding: 3rem;
color: var(--secondary-color);
}
.empty-state p {
font-size: 1.2rem;
}
/* --- 动画 --- */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(-30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
/* --- 响应式设计 --- */
@media (max-width: 600px) {
header h1 {
font-size: 2rem;
}
main {
padding: 1.5rem;
}
.message-item {
padding: 1rem;
}
.actions {
flex-direction: column;
align-items: stretch;
}
.clear-btn {
width: 100%;
}
}
JavaScript 代码 (script.js)
这是脚本文件,负责处理所有交互逻辑,如添加、显示、保存和清空留言。

(图片来源网络,侵删)
document.addEventListener('DOMContentLoaded', () => {
// --- DOM 元素 ---
const messageForm = document.getElementById('messageForm');
const nicknameInput = document.getElementById('nickname');
const contentInput = document.getElementById('content');
const messageList = document.getElementById('messageList');
const clearBtn = document.getElementById('clearBtn');
const successMessage = document.getElementById('successMessage');
// --- 初始化 ---
// 从 localStorage 加载留言
let messages = JSON.parse(localStorage.getItem('guestbook_messages')) || [];
// 如果是第一次加载(没有本地数据),则显示示例留言
if (messages.length === 0) {
const initialMessage = {
id: Date.now(), // 使用时间戳作为唯一ID
nickname: '示例用户',
content: '欢迎使用这个留言板!这是第一条示例留言,你可以删除它或添加你自己的留言。',
time: new Date().toLocaleString('zh-CN')
};
messages.push(initialMessage);
localStorage.setItem('guestbook_messages', JSON.stringify(messages));
}
// 渲染所有留言
renderMessages();
// --- 事件监听器 ---
messageForm.addEventListener('submit', handleSubmit);
clearBtn.addEventListener('click', handleClear);
// --- 函数定义 ---
/**
* 处理表单提交
*/
function handleSubmit(e) {
e.preventDefault(); // 阻止表单默认提交行为
const nickname = nicknameInput.value.trim();
const content = contentInput.value.trim();
if (nickname && content) {
const newMessage = {
id: Date.now(), // 使用时间戳作为唯一ID
nickname: nickname,
content: content,
time: new Date().toLocaleString('zh-CN')
};
// 添加到留言数组的开头
messages.unshift(newMessage);
// 保存到 localStorage
saveMessages();
// 重新渲染留言列表
renderMessages();
// 清空输入框
messageForm.reset();
nicknameInput.focus(); // 将焦点放回昵称输入框
// 显示成功提示
showSuccessMessage();
}
}
/**
* 渲染所有留言到页面上
*/
function renderMessages() {
// 清空当前列表(保留一个空的容器)
messageList.innerHTML = '';
if (messages.length === 0) {
messageList.innerHTML = '<div class="empty-state"><p>还没有留言,快来留下第一条吧!</p></div>';
return;
}
// 遍历留言数组并创建DOM元素
messages.forEach(message => {
const messageItem = document.createElement('div');
messageItem.className = 'message-item';
messageItem.innerHTML = `
<div class="message-header">
<span class="nickname">${escapeHtml(message.nickname)}</span>
<span class="time">${message.time}</span>
</div>
<div class="message-content">${escapeHtml(message.content)}</div>
`;
messageList.prepend(messageItem); // 使用 prepend 将新留言添加到顶部
});
}
/**
* 处理清空留言操作
*/
function handleClear() {
if (messages.length === 0) {
alert('留言板已经是空的了!');
return;
}
// 使用 window.confirm 进行二次确认
const isConfirmed = confirm('确定要清空所有留言吗?此操作不可恢复!');
if (isConfirmed) {
messages = [];
saveMessages();
renderMessages();
}
}
/**
* 将留言数据保存到 localStorage
*/
function saveMessages() {
localStorage.setItem('guestbook_messages', JSON.stringify(messages));
}
/**
* 显示成功提示,并在几秒后自动隐藏
*/
function showSuccessMessage() {
successMessage.classList.add('show');
setTimeout(() => {
successMessage.classList.remove('show');
}, 3000); // 3秒后隐藏
}
/**
* 防止XSS攻击的简单转义函数
* @param {string} str - 需要转义的字符串
* @returns {string} - 转义后的安全字符串
*/
function escapeHtml(str) {
return str
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
});
如何运行
- 创建文件:在你的电脑上创建一个新文件夹,
guestbook。 - 保存代码:将上面的 HTML、CSS 和 JavaScript 代码分别复制并保存到以下文件中,确保文件名正确:
guestbook/index.htmlguestbook/style.cssguestbook/script.js
- 打开网页:用 Chrome、Firefox、Edge 等现代浏览器打开
index.html文件。
你就拥有了一个功能完整、设计美观的留言板了!你可以尝试添加留言、刷新页面、清空留言,感受它的功能。

(图片来源网络,侵删)
