使用JS编写一个好看的用户信息收集表单
实现效果
实现代码
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户信息收集表单</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f7f7f7;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 20px;
color: #333;
}
.form-container {
background-color: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 600px;
padding: 30px;
}
h1 {
color: #2c3e50;
text-align: center;
margin-bottom: 25px;
font-weight: 500;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: #2c3e50;
}
input[type="text"],
input[type="number"] {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
transition: border-color 0.3s;
}
input:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
}
.error-input {
border-color: #e74c3c;
}
.error-input:focus {
box-shadow: 0 0 0 2px rgba(231, 76, 60, 0.2);
}
.radio-group, .checkbox-group {
display: flex;
gap: 20px;
margin-top: 8px;
}
.radio-group label, .checkbox-group label {
display: flex;
align-items: center;
gap: 8px;
font-weight: normal;
cursor: pointer;
}
input[type="radio"], input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
}
.error-message {
color: #e74c3c;
font-size: 14px;
margin-top: 6px;
display: none;
}
button {
background-color: #3498db;
color: white;
border: none;
border-radius: 4px;
padding: 14px 25px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
display: block;
width: 100%;
}
button:hover {
background-color: #2980b9;
}
.output-container {
margin-top: 30px;
}
.output-box {
margin-bottom: 15px;
}
textarea {
width: 100%;
height: 120px;
padding: 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-family: inherit;
resize: vertical;
}
textarea:focus {
outline: none;
border-color: #3498db;
}
</style>
</head>
<body>
<div class="form-container">
<h1>用户信息收集</h1>
<form id="userForm">
<div class="form-group">
<label for="name">Name:</label>
<input type="text" id="name" name="name">
<div class="error-message" id="nameError"></div>
</div>
<div class="form-group">
<label for="age">Age:</label>
<input type="number" id="age" name="age" min="0">
<div class="error-message" id="ageError"></div>
</div>
<div class="form-group">
<label>Gender</label>
<div class="radio-group">
<label>
<input type="radio" name="gender" value="male"> Male
</label>
<label>
<input type="radio" name="gender" value="female"> Female
</label>
</div>
<div class="error-message" id="genderError"></div>
</div>
<div class="form-group">
<label for="phone">Phone:</label>
<input type="text" id="phone" name="phone">
</div>
<div class="form-group">
<label>Hobbies</label>
<div class="checkbox-group">
<label>
<input type="checkbox" name="hobby" value="reading"> Reading
</label>
<label>
<input type="checkbox" name="hobby" value="sports"> Sports
</label>
<label>
<input type="checkbox" name="hobby" value="music"> Music
</label>
</div>
</div>
<button type="button" id="submitBtn">Submit</button>
</form>
<div class="output-container">
<div class="output-box">
<label>Output:</label>
<textarea id="output" readonly></textarea>
</div>
<div class="output-box">
<label>Errors:</label>
<textarea id="errors" readonly></textarea>
</div>
</div>
</div>
<script>
document.getElementById('submitBtn').addEventListener('click', function() {
// Clear previous errors and outputs
document.getElementById('output').value = '';
document.getElementById('errors').value = '';
// Clear error messages and input styling
document.querySelectorAll('.error-message').forEach(el => {
el.textContent = '';
el.style.display = 'none';
});
document.querySelectorAll('input').forEach(input => {
input.classList.remove('error-input');
});
// Collect form data
const name = document.getElementById('name').value.trim();
const age = document.getElementById('age').value;
const gender = document.querySelector('input[name="gender"]:checked')?.value;
const phone = document.getElementById('phone').value.trim();
// Get selected hobbies
const hobbies = Array.from(document.querySelectorAll('input[name="hobby"]:checked'))
.map(hobby => hobby.value);
// Validate form
let isValid = true;
const errorMessages = [];
// Validate Name
if (!name) {
showError('nameError', 'Name cannot be blank');
isValid = false;
} else if (name.length > 15) {
showError('nameError', 'Name cannot exceed 15 letters');
isValid = false;
}
// Validate Age
if (!age) {
showError('ageError', 'Age cannot be blank');
isValid = false;
} else {
const ageNum = Number(age);
if (isNaN(ageNum)) {
showError('ageError', 'Age must be a number');
isValid = false;
} else if (ageNum < 0) {
showError('ageError', 'Age cannot be negative');
isValid = false;
}
}
// Validate Gender
if (!gender) {
showError('genderError', 'Please select a gender');
isValid = false;
}
// Process form or display errors
if (isValid) {
// Format the output data
const output = [
`Name: ${name}`,
`Age: ${age}`,
`Gender: ${gender.charAt(0).toUpperCase() + gender.slice(1)}`,
`Phone: ${phone || 'Not provided'}`,
`Hobbies: ${hobbies.length > 0 ? hobbies.join(', ') : 'None selected'}`
].join('\n');
document.getElementById('output').value = output;
} else {
document.getElementById('errors').value = errorMessages.join('\n');
}
});
// Helper function to display validation errors
function showError(elementId, message) {
const errorElement = document.getElementById(elementId);
errorElement.textContent = message;
errorElement.style.display = 'block';
// Highlight error inputs based on error type
if (elementId === 'nameError') {
document.getElementById('name').classList.add('error-input');
} else if (elementId === 'ageError') {
document.getElementById('age').classList.add('error-input');
} else if (elementId === 'genderError') {
document.querySelectorAll('input[name="gender"]').forEach(el => {
el.closest('label').classList.add('error-input');
});
}
// Add error message to list
errorMessages.push(message);
}
</script>
</body>
</html>
🖌️ 视觉篇:颜值即正义
css
/* 魔法起始!整个页面变成优雅的居中考场 */
body {
display: flex; /* 开启弹性布局魔法 */
justify-content: center; /* 左右居中 */
align-items: center; /* 垂直居中 */
background-color: #f7f7f7; /* 温柔的灰色背景 */
}
/* 表单容器秒变精致卡片 */
.form-container {
box-shadow: 0 4px 12px rgba(0,0,0,0.1); /* 阴影:轻盈的漂浮感 */
border-radius: 8px; /* 圆角:拒绝尖锐伤害 */
}
设计亮点:
- 输入框获得焦点时会出现蓝色光环
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2)
,像被魔法棒点中一样✨ - 错误输入框秒变"番茄色"警告:
.error-input { border-color: #e74c3c }
- 提交按钮悬停时颜色变深,仿佛在说:"摸我一下试试?" 👆
📝 结构篇:HTML的智慧组合
js
<!-- 分组管理就像文件柜 -->
<div class="form-group">
<label for="name">Name:</label> <!-- 贴心标签 -->
<input type="text" id="name"> <!-- 文本输入 -->
<div class="error-message"></div> <!-- 预留错误展示位 -->
</div>
<!-- 单选组横向排列不打架 -->
<div class="radio-group">
<label><input type="radio" name="gender" value="male"> Male</label>
<label><input type="radio" name="gender" value="female"> Female</label>
</div>
<!-- 兴趣爱好像个自助餐 -->
<div class="checkbox-group">
<label><input type="checkbox" name="hobby" value="reading"> Reading</label>
<!-- 其他选项... -->
</div>
小心机设计:
name
属性相同的radio自动成组(只能选一个)- 多选框
name
相同但允许多选,就像"我全都要!" 😏 - 错误提示一开始是隐藏的
display: none
,只在需要时跳出来
⚡ 互动篇:JavaScript的表演时刻
js
// 提交按钮:点我就开始表演!
submitBtn.addEventListener('click', () => {
// 开场先清场打扫
document.querySelectorAll('.error-message').forEach(el => {
el.style.display = 'none';
});
// 开启侦探模式查案
const name = nameInput.value.trim();
if (!name) showError('nameError', '名字呢?你忘填啦!');
else if (name.length > 15) showError('nameError', '名字太长记不住啦!');
const age = ageInput.value;
if (!age) showError('ageError', '年龄可是必考题!');
else if (age < 0) showError('ageError', '咋的?来自未来?');
});
验证逻辑三巨头:
- 姓名:不能为空且≤15字符(超过时提示:"名字太长记不住啦!")
- 年龄:必须为≥0的数字(负值会吐槽:"咋的?来自未来?")
- 性别:必须选择(否则警告:"漏掉性别了亲!")
成功后:
- 在
<textarea>
里美美展示用户信息 - 空手机号显示"Not provided",而不是尴尬的空白
- 爱好自动拼接:
hobbies.join(', ')
→ "Reading, Music"
💡 用户体验的闪光点
- 实时反馈:错误输入框秒变红色+文字提示(像有个小助手在耳边提醒)
- 包容设计:电话和爱好是可选项(用户:"终于不用编手机号了!")
- 错误分屏展示:成功时只显示结果,失败时单独列出错误日志
- 性别单选组:排排站的设计比下拉菜单更直观
🚀 精妙代码技巧
js
// 1. 优雅获取单选值
const gender = document.querySelector('input[name="gender"]:checked')?.value;
// 2. 获取多个复选框的黑科技
const hobbies = [...document.querySelectorAll('input[name="hobby"]:checked')]
.map(hobby => hobby.value);
// 3. 错误处理函数(复用大师)
function showError(elementId, message) {
const el = document.getElementById(elementId);
el.textContent = message;
el.style.display = 'block'; // 让错误信息闪亮登场
}
结语:这个表单会说话! 💬
这个表单最棒的地方在于:它懂用户更懂开发者。
- 用户看到的是优雅的卡片、聪明的提示和友好的反馈
- 开发者看到的是清晰的代码结构、灵活的验证逻辑和易于维护的设计
下次做表单时,记得把这些小心思偷走哦!这可比干巴巴的<input>
标签酷多啦~