- HTML 结构:创建登录表单。
- CSS 样式:美化页面,使其看起来专业和现代。
- JavaScript 交互:实现表单验证、登录逻辑和页面反馈。
最终效果预览
这是一个我们最终要实现的效果,它包含了:

(图片来源网络,侵删)
- 现代化的UI设计
- 客户端表单验证(用户名/密码非空)
- 模拟的后端登录验证(正确的用户名是
admin,密码是password123) - 登录成功后跳转到欢迎页面
- 登录失败后显示错误提示
- 加载状态指示器
第 1 步:HTML 结构 (index.html)
我们创建一个基本的 HTML 文件,包含登录表单,表单中需要有用户名、密码输入框,以及登录按钮。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">登录页面</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="login-container">
<form id="loginForm" class="login-form">
<h2>用户登录</h2>
<!-- 错误提示区域 -->
<div id="errorMessage" class="error-message"></div>
<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" name="username" placeholder="请输入用户名" required>
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" id="password" name="password" placeholder="请输入密码" required>
</div>
<button type="submit" id="loginButton" class="login-button">
<span id="buttonText">登录</span>
<span id="loadingSpinner" class="spinner" style="display: none;"></span>
</button>
</form>
</div>
<!-- 用于登录后跳转的欢迎页面 (初始隐藏) -->
<div id="welcomePage" class="welcome-page" style="display: none;">
<h1>欢迎回来, <span id="welcomeUsername"></span>!</h1>
<p>登录成功。</p>
<button onclick="logout()">退出登录</button>
</div>
<script src="script.js"></script>
</body>
</html>
第 2 步:CSS 样式 (style.css)
我们用 CSS 来美化这个登录页面,使其更具吸引力。
/* 全局样式和变量 */
:root {
--primary-color: #4a90e2;
--error-color: #e74c3c;
--success-color: #2ecc71;
--text-color: #333;
--light-gray: #ecf0f1;
--border-color: #bdc3c7;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f4f7f6;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
color: var(--text-color);
}
/* 登录容器 */
.login-container {
background-color: white;
padding: 40px;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 400px;
}
/* 表单样式 */
.login-form h2 {
text-align: center;
margin-bottom: 25px;
color: var(--primary-color);
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 600;
}
.form-group input {
width: 100%;
padding: 12px;
border: 1px solid var(--border-color);
border-radius: 5px;
box-sizing: border-box; /* 确保padding不会影响宽度 */
font-size: 16px;
transition: border-color 0.3s;
}
.form-group input:focus {
outline: none;
border-color: var(--primary-color);
}
/* 错误信息样式 */
.error-message {
color: var(--error-color);
text-align: center;
margin-bottom: 20px;
font-size: 14px;
height: 20px; /* 固定高度,防止页面跳动 */
}
/* 登录按钮样式 */
.login-button {
width: 100%;
padding: 12px;
background-color: var(--primary-color);
border: none;
border-radius: 5px;
color: white;
font-size: 16px;
font-weight: bold;
cursor: pointer;
position: relative; /* 用于定位加载动画 */
transition: background-color 0.3s;
}
.login-button:hover {
background-color: #357abd;
}
/* 加载动画 (spinner) */
.spinner {
border: 2px solid #f3f3f3;
border-top: 2px solid white;
border-radius: 50%;
width: 16px;
height: 16px;
animation: spin 1s linear infinite;
position: absolute;
right: 15px;
top: 50%;
transform: translateY(-50%);
}
@keyframes spin {
0% { transform: translateY(-50%) rotate(0deg); }
100% { transform: translateY(-50%) rotate(360deg); }
}
/* 欢迎页面样式 */
.welcome-page {
text-align: center;
background-color: white;
padding: 40px;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
.welcome-page h1 {
color: var(--success-color);
}
.welcome-page button {
margin-top: 20px;
padding: 10px 20px;
border: none;
border-radius: 5px;
background-color: var(--primary-color);
color: white;
cursor: pointer;
}
第 3 步:JavaScript 交互 (script.js)
这是核心部分,我们将在这里实现所有的登录逻辑。
// 获取DOM元素
const loginForm = document.getElementById('loginForm');
const usernameInput = document.getElementById('username');
const passwordInput = document.getElementById('password');
const loginButton = document.getElementById('loginButton');
const buttonText = document.getElementById('buttonText');
const loadingSpinner = document.getElementById('loadingSpinner');
const errorMessage = document.getElementById('errorMessage');
const loginContainer = document.querySelector('.login-container');
const welcomePage = document.getElementById('welcomePage');
const welcomeUsername = document.getElementById('welcomeUsername');
// 模拟的用户数据库 (在实际项目中,这是从服务器获取的)
const users = {
'admin': 'password123',
'user1': '123456'
};
// 模拟的API请求延迟 (网络请求需要时间)
const API_DELAY = 1500; // 1.5秒
// 监听表单提交事件
loginForm.addEventListener('submit', function(event) {
// 阻止表单默认的提交行为 (页面刷新)
event.preventDefault();
// 1. 获取输入值
const username = usernameInput.value.trim();
const password = passwordInput.value;
// 2. 前端验证
if (!username || !password) {
showError('用户名和密码不能为空!');
return;
}
// 3. 显示加载状态
setLoadingState(true);
// 4. 模拟异步登录请求
setTimeout(() => {
// 5. 验证用户名和密码
if (users[username] && users[username] === password) {
// 登录成功
loginSuccess(username);
} else {
// 登录失败
loginFailed();
}
// 无论成功失败,都要恢复按钮状态
setLoadingState(false);
}, API_DELAY);
});
// 显示错误信息
function showError(message) {
errorMessage.textContent = message;
errorMessage.style.display = 'block';
}
// 隐藏错误信息
function hideError() {
errorMessage.style.display = 'none';
}
// 设置按钮为加载状态
function setLoadingState(isLoading) {
if (isLoading) {
loginButton.disabled = true; // 禁用按钮,防止重复点击
buttonText.textContent = '登录中...';
loadingSpinner.style.display = 'inline-block';
hideError(); // 在新请求开始时隐藏旧错误
} else {
loginButton.disabled = false; // 重新启用按钮
buttonText.textContent = '登录';
loadingSpinner.style.display = 'none';
}
}
// 登录成功处理
function loginSuccess(username) {
hideError();
// 在实际应用中,这里通常会保存一个 token (localStorage)
localStorage.setItem('isLoggedIn', 'true');
localStorage.setItem('currentUser', username);
// 显示欢迎页面,隐藏登录表单
welcomeUsername.textContent = username;
loginContainer.style.display = 'none';
welcomePage.style.display = 'block';
}
// 登录失败处理
function loginFailed() {
showError('用户名或密码错误!');
// (可选) 清空密码框
passwordInput.value = '';
// (可选) 聚焦到密码框
passwordInput.focus();
}
// 退出登录函数
function logout() {
// 清除登录状态
localStorage.removeItem('isLoggedIn');
localStorage.removeItem('currentUser');
// 显示登录表单,隐藏欢迎页面
welcomePage.style.display = 'none';
loginContainer.style.display = 'block';
// 清空输入框
usernameInput.value = '';
passwordInput.value = '';
usernameInput.focus();
}
// 页面加载时检查是否已经登录 (可选,但推荐)
window.addEventListener('DOMContentLoaded', () => {
const isLoggedIn = localStorage.getItem('isLoggedIn');
const currentUser = localStorage.getItem('currentUser');
if (isLoggedIn === 'true' && currentUser) {
// 如果已经登录,直接显示欢迎页面
welcomeUsername.textContent = currentUser;
loginContainer.style.display = 'none';
welcomePage.style.display = 'block';
} else {
// 否则,聚焦到用户名输入框
usernameInput.focus();
}
});
如何运行
- 创建一个项目文件夹,
login-page。 - 在该文件夹中创建三个文件:
index.html、style.css和script.js。 - 将上面的代码分别复制到对应的文件中。
- 用浏览器打开
index.html文件即可看到效果。
代码解析和关键点
event.preventDefault():这是最关键的一步,它阻止了表单提交时浏览器默认的刷新页面的行为,让我们有机会用 JavaScript 来处理数据。- DOM 操作:通过
document.getElementById或更现代的document.querySelector获取页面上的元素,以便在 JavaScript 中操作它们(如修改文本、样式、显示/隐藏等)。 setTimeout:用于模拟网络请求的延迟,真实的登录请求是异步的,你需要等待服务器返回结果,而不是立即得到。setTimeout让我们可以在不真正联网的情况下体验这个过程。- 状态管理:
- 加载状态:通过修改按钮的文本、显示加载动画和禁用按钮,给用户一个“正在处理”的反馈,防止用户重复点击。
- 错误/成功状态:通过修改
errorMessage的内容和loginContainer/welcomePage的display样式,来向用户展示不同的结果。
localStorage:这是一个浏览器提供的简单本地存储,我们用它来模拟“登录状态”的持久化,当你关闭浏览器再打开时,window.addEventListener('DOMContentLoaded', ...)会检查localStorage,如果发现你已登录,就会直接让你进入欢迎页面,这是实现“记住我”功能最基础的方式。- 用户体验:
- 表单验证在提交前进行,可以快速给用户反馈。
- 登录失败后清空密码框并聚焦,提升了用户再次输入的便利性。
- 加载动画提供了重要的视觉反馈。
进一步的改进方向
- 真正的后端 API:将
users对象和验证逻辑替换为fetch或axios发送 HTTP 请求到真实的服务器(如 Node.js, Python, PHP 等)。 - 密码加密:在前端发送密码前,应该使用哈希算法(如 SHA-256)进行一次加密,避免明文传输密码,但更安全的做法是让后端处理所有加密和验证。
- 更复杂的验证:如邮箱格式验证、密码强度要求等。
- “记住我”功能:可以通过设置
localStorage的过期时间或使用Cookie来实现。 - 路由:如果这是一个单页应用,可以使用
react-router或vue-router这样的库来管理页面跳转,而不是直接操作display样式。

(图片来源网络,侵删)
