前端学习4:小白入门注册表单的制作(包括详细思考CSS、JS实现过程)
这篇我们来做一个简单表单,即常见的注册页面吧~学习完这篇我们将学习到Input、label、CSS伪类、CSS入门、更多的JS操作等。。
一、首先明确需求:
直接模仿常见的注册页面,包括:用户名、Email、性别(单选按钮男/女)、出生日期(点击出现日期表)、密码、确认密码和提交按钮。
二、实现思路分析
表单结构设计:
使用HTML的
<form>
标签作为容器每个输入项用
<div>
包裹,包含<label>
和<input>
需要不同类型的输入控件:文本、密码、邮箱、单选、日期等
样式设计:
添加响应式设计,适应不同屏幕
添加交互反馈(如聚焦效果)
JavaScript功能:
表单验证(前端验证)
密码一致性检查
日期选择器功能
表单提交处理
三、HTML结构:
<!DOCTYPE html>
<html>
<head><title>注册表单</title><link rel="stylesheet" href="form.css">
</head>
<body><div class="form-container"><h2>用户注册</h2><form id="registerForm"><!-- 用户名 --><div class="form-group"><label for="username">用户名:</label><input type="text" id="username" name="username" required><span class="error-message" id="usernameError"></span></div><!-- 邮箱 --><div class="form-group"><label for="email">Email:</label><input type="email" id="email" name="email" required><span class="error-message" id="emailError"></span></div><!-- 性别 --><div class="form-group"><label>性别:</label><div class="radio-group"><input type="radio" id="male" name="gender" value="male" checked><label for="male">男</label><input type="radio" id="female" name="gender" value="female"><label for="female">女</label></div></div><!-- 出生日期 --><div class="form-group"><label for="birthdate">出生日期:</label><input type="date" id="birthdate" name="birthdate" required></div><!-- 密码 --><div class="form-group"><label for="password">密码:</label><input type="password" id="password" name="password" required><span class="error-message" id="passwordError"></span></div><!-- 确认密码 --><div class="form-group"><label for="confirmPassword">确认密码:</label><input type="password" id="confirmPassword" name="confirmPassword" required><span class="error-message" id="confirmPasswordError"></span></div><button type="submit" class="submit-btn">注册</button></form></div><script src="form.js"></script>
</body>
</html>
1. <label>
标签详解:
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required>
<label>
是表单元素的文字说明,可以提升用户体验:点击label文字也能聚焦到对应的输入框
for
的值必须与对应<input>
的id
值完全一致,这样浏览器就知道这个label是描述哪个input的点击"用户名:"文字时,会自动聚焦到id为"username"的输入框
2. <input>
标签详解
<input type="text" id="username" name="username" required>
关键属性解析:
属性 | 作用 | 示例值 |
---|---|---|
type | 定义输入框类型 | text (文本)、password (密码)、email (邮箱)等 |
id | 唯一标识符,用于label关联和CSS/JS操作 | username |
name | 表单提交时的字段名(服务器接收的参数名) | username |
required | 表示必填项(HTML5验证) | 不需要值 |
备注:id
和name
经常设成相同,但作用不同:
id
是给浏览器/DOM用的name
是给表单提交到服务器用的(是服务器接收的参数名)
3. 错误提示<span>
的作用
<span class="error-message" id="usernameError"></span>
这个空容器用来显示JS验证的错误提示。
4.性别的单选按钮(radio)详解:
<div class="form-group">
<label>性别:</label>
<div class="radio-group">
<input type="radio" id="male" name="gender" value="male" checked>
<label for="male">男</label>
<input type="radio" id="female" name="gender" value="female">
<label for="female">女</label>
</div>
</div>
radio-group
整个div包含两个单选按钮及其对应标签单选按钮(input radio)基本属性(重要):
type="radio"
:定义这是一个单选按钮id="male"
:唯一标识符,用于关联labelname="gender"
:分组名称,相同name的单选按钮为一组value="male"
:选中时提交的值checked
:默认选中属性
配套label标签:
<label for="male">男</label>
for="male"
:与input的id对应,点击label也能选中单选按钮(前面有说!)"男":显示给用户看的文本
为什么男和女的name属性相同?----相同
name
值使它们成为一组,只能选其中一个,如果name不同,就失去了单选的意义关于这块的一些常见疑问:
Q1:为什么不用select下拉菜单而用radio?
A1:radio适合选项较少(2-5个)且需要直观展示所有选项的情况,select适合选项较多的情况。
Q2:checked属性可以加在多个radio上吗?
A2:不可以,同name的radio组中只有一个能被checked,浏览器会自动处理。
Q3:如何设置默认不选中任何选项?
A3:去掉所有radio的checked属性即可,但通常建议设置一个默认选中项。
Q4:value属性是必须的吗?
A4:是的,否则表单提交时该字段的值会是"on"而不是你期望的值。
四、CSS结构:
本次必须讲解下CSS的编写的思路!(严肃脸)
步骤1:分析页面结构
前面我们写好了html,搭好了结构,现在装修下,但是怎么“装修”呢?先分析下我们html页面的一个整体结构涉及到的class=“xxx”,可以发现,基本都是这样的结构:
表单容器(form-container)
├─ 标题(h2)
└─ 表单(form)
├─ 表单项1(form-group)
│ ├─ 标签(label)
│ └─ 输入框(input)
└─ 表单项2........
步骤2:按照思路:从外到内,从大到小编写CSS
先写最外层容器样式
然后写内部大区块
最后处理细节和小元素
脑子里先构建好大致的长什么样?比如我们现在就做最简单的一个表:
(1)先从第一个最外层的容器——form-container:
(以下都按三步法:确定设计图-->思考过程-->实现代码)
First:构建图:
Second:思考过程:
需要白色背景 →
background-color:
white;需要和里面的内容有间距 →
padding(内边距)
需要圆角效果 →
border-radius
需要阴影提升层次感 →
box-shadow,
一般都是用box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);在手机上占满宽度,但不超过500px →
width:100%
+max-width
Third:总结就是:
.form-container {background-color: white;padding: 30px;border-radius: 8px;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);width: 100%; /* 宽度 */max-width: 500px; /* 最大宽度 */
}
(2) 表单项.form-group
First:构建图:
Second:思考过程:
每个表单项(用户名、邮箱、密码...)之间需要垂直间距
不需要左右margin因为父容器已经有padding
不需要上margin因为第一个元素不需要顶部间距
Third:总结就是:
.form-group {margin-bottom: 20px;
}
(3)标签label:
其次就是标签这一块,即“用户名”、“密码”等等这一块搞个统一的样式:
First:构建图:
Second:思考过程:
- 我们需要这独占一行 ,而
<label>
元素默认属于行内元素,所以我们需要转成块级元素(搞不明白的去学我之前的文章块级和行内的区别):display: block; - 然后文字跟框有距离:margin-bottom(如图中的橙色部分)
- 字体设置为粗体,醒目:font-weight: bold;
- 字的颜色设置为黑色:color
Third:总结就是:
label {display: block;margin-bottom: 8px;font-weight: bold;color: #555;
}
(4)输入框样式 (重要)
先学习下新写法——属性选择器input[type="xxx"]
:精确选择特定类型的input
First:构建图:
Second:思考过程:
- 框需要充满父容器 :width:100%
- 文字与框之间需要距离,即内边距padding
- 框我们想要黑边的、圆角的:border和border-radius,一般大圆角就是8px,小圆角就是4px
- 输入的内容的字体我们也可以控制下:font-size
- 还有一个重要的点,因为我们需要框刚好去充满父容器,所以需要保证内边距+边框的宽度是不会影响整个总宽度的:box-sizing: border-box
Third:总结就是:
input[type="text"],
input[type="email"],
input[type="password"],
input[type="date"] {width: 100%; /* 充满父容器 */padding: 10px; /* 内边距 */border: 1px solid #ddd; /* 边框 */border-radius: 4px; /* 圆角 */font-size: 16px; /* 字体大小 */box-sizing: border-box; /* 盒模型计算方式:让width包含padding和border */
}
(5)单选按钮组:
First:构建图:
Second:思考过程:
先看整个radio-group(包括了按钮和标签):
- 我们想要水平放置按钮: display: flex;
- 两个按钮需要有间距:gap
- 与上方的标签“性别”需要有距离(外边距):margin-top(图中橙色的那部分)
再看radio-group的标签(也就是“男”,“女”):
- 同样也需要水平放置:display: flex;
- 与上一个按钮之间需要间距,不然贴在一起不好看:gap
好,看下结果却是:
发现“男”、“女”两个字“飘”上去了,有个margin-bottom: 8px;存在,对不齐,太丑了。
为什么会有margin-bottom: 8px这个存在呢?因为男女也是属于label,而前面我们设置了label中的margin-bottom: 8px;,因此这个男女也被应用上了,所以!我们需要在这个男女这个标签单独去加个margin-bottom: 0;!
还有同样的取消掉加粗:font-weight: normal;
Third:总结就是:
.radio-group {display: flex; /* 使用flex布局让单选按钮水平排列 */gap: 20px; /* 设置20px的间距 */margin-top: 8px; /* 与上方标签的间距 */
}.radio-group label {display: flex;gap: 5px; /* 文字和按钮间距 */font-weight: normal; /* 取消加粗 */margin-bottom: 0;
}
总结一个小的思考路线:
是否需要布局? → 是 → 需要水平排列吗? → 是 → display: flex
│ → 否 → display: block
→ 否 → 需要间距吗? → 是 → margin/padding/gap
→ 否 → 需要美化吗? → 是 → 颜色/圆角/阴影...
CSS的全部代码(form.css):
body {font-family: 'Arial', sans-serif;background-color: #f5f5f5;display: flex;justify-content: center;align-items: center;min-height: 100vh;margin: 0;
}.form-container {background-color: white;padding: 30px;border-radius: 8px;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);width: 100%; /* 宽度 */max-width: 500px; /* 最大宽度 */
}h2 {text-align: center;color: #333;margin-bottom: 20px;
}.form-group {margin-bottom: 20px;
}label {display: block;margin-bottom: 8px;font-weight: bold;color: #555;
}input[type="text"],
input[type="email"],
input[type="password"],
input[type="date"] {width: 100%; /* 充满父容器 */padding: 10px; /* 内边距 */border: 1px solid #ddd; /* 边框 */border-radius: 4px; /* 圆角 */font-size: 16px; /* 字体大小 */box-sizing: border-box; /* 确保内边距和边框不影响总宽度 */
}input:focus { /* input:focus 选择元素输入后具有焦点 */outline: none; /* 移除默认焦点轮廓线 */border-color: #4a90e2; /* 聚焦时的边框颜色 */box-shadow: 0 0 5px rgba(74, 144, 226, 0.5); /* 添加聚焦时的阴影效果 */
}.radio-group {display: flex; /* 使用flex布局让单选按钮水平排列 */gap: 20px; /* 设置20px的间距 */margin-top: 8px; /* 与上方标签的间距 */
}.radio-group label {display: flex;gap: 5px; /* 文字和按钮间距 */font-weight: normal; /* 取消加粗 */margin-bottom: 0;
}.error-message {color: #e74c3c;font-size: 14px;margin-top: 5px;display: block;
}.submit-btn {width: 100%;padding: 12px;background-color: #4a90e2;color: white;border: none; /* 移除默认边框 */border-radius: 4px;font-size: 16px;cursor: pointer; /* 鼠标悬停时显示手型光标 */transition: background-color 0.3s;
}.submit-btn:hover {background-color: #357ab8;
}
一个答疑总结:
Q:width:100%和width:auto有什么区别?
A:
100%
:强制撑满父容器,包含padding和borderauto
:根据内容自动计算,可能不会占满宽度
五、JavaScript功能实现:
总体流程:
首先还是我们常用的DOMContentLoaded事件(还是那句话:让HTML文档完全加载和解析完成后,再执行里面的代码,这样可以确保我们能找到DOM元素。)
document.addEventListener('DOMContentLoaded', function() {// 代码在这里执行
});
这一块的逻辑可以想想:当用户点击提交表单的按钮时,系统直接获取用户输入的所有数据,进行数据有效性的验证,然后提示注册成功,最后重置表单。大概就是这样的一个流程了。
第一步:处理点击提交按钮的事件监听:
form.addEventListener('submit', function(e) {e.preventDefault(); // 阻止表单默认提交行为
});
这里我们先写上阻止表单默认的提交行为(页面跳转),这样我们就能用JavaScript后续处理表单数据。
第二步: 获取用户输入的所有数据,需要使用到键值对,比如username对应document.getElementById('username').value,这里的.value可以拿到用户输入的值,因此我们直接把这些一个个键值对整合到一个对象里面:
// 获取表单数据const formData = {username: document.getElementById('username').value,email: document.getElementById('email').value,gender: document.querySelector('input[name="gender"]:checked').value,birthdate: document.getElementById('birthdate').value,password: document.getElementById('password').value};
formData
对象的作用是收集表单数据,把各个表单字段的值(如用户名、邮箱)整合到一个对象中。
第三步:数据有效性验证,这里我们是想校验用户输入的用户名比如要求在4-20个字符内等等要求,所以我们需要对每个字段数据去校验。把校验的功能单独分出来,后续再具体写,这里先假装调用函数:
if (validateForm()) {
。。。
}
第四步:提示注册成功:
alert('注册成功!');
第五步:重置表单:
form.reset();
整体代码如下:
document.addEventListener('DOMContentLoaded', function() {const form = document.getElementById('registerForm');// 表单提交事件处理form.addEventListener('submit', function(e) {e.preventDefault(); // 阻止表单默认提交行为// 验证表单if (validateForm()) {// 获取表单数据const formData = {username: document.getElementById('username').value,email: document.getElementById('email').value,// 使用CSS选择器找到被选中的单选按钮,获取其valuegender: document.querySelector('input[name="gender"]:checked').value,birthdate: document.getElementById('birthdate').value,password: document.getElementById('password').value};// 这里通常是发送到服务器的代码console.log('表单数据:', formData);alert('注册成功!');form.reset(); // 重置表单}});
表单验证函数validateForm():
然后我们具体来写表单验证函数validateForm(),先定义函数function validateForm(){...}
定义一个标志,比如验证用户输入的用户名是4-20个字符里面,就设置标志为true,验证密码至少6个字符,就设置为true,一一验证,最后返回标志,这样回到前面我们的if (validateForm())就可以判断是不是true,从而走下面的步骤。
// 表单验证函数function validateForm() {let isValid = true;// 验证用户名const username = document.getElementById('username').value;if (username.length < 4 || username.length > 20) {document.getElementById('usernameError').textContent = '用户名需4-20个字符';isValid = false;}// 验证邮箱const email = document.getElementById('email').value;const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;if (!emailRegex.test(email)) {document.getElementById('emailError').textContent = '请输入有效的邮箱地址';isValid = false;}// 验证密码const password = document.getElementById('password').value;if (password.length < 6) {document.getElementById('passwordError').textContent = '密码至少6个字符';isValid = false;}// 验证确认密码const confirmPassword = document.getElementById('confirmPassword').value;if (confirmPassword !== password) {document.getElementById('confirmPasswordError').textContent = '两次输入的密码不一致';isValid = false;}return isValid;}
总结一下前面的代码都在干嘛?-----就是用户点击提交按钮,我们去校验每个字段是不是合乎我们自定义的规则的?是就提示“注册成功”。
但是我们想继续实时校验每个字段数据呢?就是比如用户输入第一个用户名不符合规则,就提示,而不是等到点击提交按钮了再统一提示。
全部代码如下(form.js):
// script.js
document.addEventListener('DOMContentLoaded', function() {const form = document.getElementById('registerForm');// 表单提交事件处理form.addEventListener('submit', function(e) {e.preventDefault(); // 阻止表单默认提交行为// 验证表单if (validateForm()) {// 获取表单数据const formData = {username: document.getElementById('username').value,email: document.getElementById('email').value,gender: document.querySelector('input[name="gender"]:checked').value,birthdate: document.getElementById('birthdate').value,password: document.getElementById('password').value};// 这里通常是发送到服务器的代码console.log('表单数据:', formData);alert('注册成功!');form.reset(); // 重置表单}});// 实时验证用户名document.getElementById('username').addEventListener('input', function() {const username = this.value;const errorElement = document.getElementById('usernameError');if (username.length < 4) {errorElement.textContent = '用户名至少4个字符';} else if (username.length > 20) {errorElement.textContent = '用户名不能超过20个字符';} else {errorElement.textContent = '';}});// 实时验证邮箱document.getElementById('email').addEventListener('input', function() {const email = this.value;const errorElement = document.getElementById('emailError');const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;if (!emailRegex.test(email)) {errorElement.textContent = '请输入有效的邮箱地址';} else {errorElement.textContent = '';}});// 密码验证document.getElementById('password').addEventListener('input', function() {const password = this.value;const errorElement = document.getElementById('passwordError');if (password.length < 6) {errorElement.textContent = '密码至少6个字符';} else {errorElement.textContent = '';}});// 确认密码验证document.getElementById('confirmPassword').addEventListener('input', function() {const confirmPassword = this.value;const password = document.getElementById('password').value;const errorElement = document.getElementById('confirmPasswordError');if (confirmPassword !== password) {errorElement.textContent = '两次输入的密码不一致';} else {errorElement.textContent = '';}});// 表单验证函数function validateForm() {let isValid = true;// 验证用户名const username = document.getElementById('username').value;if (username.length < 4 || username.length > 20) {document.getElementById('usernameError').textContent = '用户名需4-20个字符';isValid = false;}// 验证邮箱const email = document.getElementById('email').value;const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;if (!emailRegex.test(email)) {document.getElementById('emailError').textContent = '请输入有效的邮箱地址';isValid = false;}// 验证密码const password = document.getElementById('password').value;if (password.length < 6) {document.getElementById('passwordError').textContent = '密码至少6个字符';isValid = false;}// 验证确认密码const confirmPassword = document.getElementById('confirmPassword').value;if (confirmPassword !== password) {document.getElementById('confirmPasswordError').textContent = '两次输入的密码不一致';isValid = false;}return isValid;}
});