在现代 Web 开发中,表单是用户与网站互动的重要桥梁。传统的表单提交方式会导致页面刷新,影响用户体验。本文将深入探讨如何使用 JavaScript 提升表单交互的艺术,实现优雅的无刷新提交,并结合实例讲解关键技术点。
1. e.currentTarget 与 e.preventDefault():事件处理的基石
在处理表单提交事件时,e.currentTarget 和 e.preventDefault() 是两个非常重要的概念。
-
e.currentTarget: 指的是绑定事件的元素。在表单的submit事件中,如果事件直接绑定在<form>元素上,则e.currentTarget就是form元素本身。如果事件绑定在 form 表单内部的某个元素上,例如 button,则e.currentTarget指向绑定事件的元素。 -
e.preventDefault(): 用于阻止事件的默认行为。对于submit事件来说,默认行为是页面刷新。使用e.preventDefault()可以阻止这种默认行为,从而实现无刷新提交。
此外,我们还需要重点关注 e.currentTarget 上的 form 属性。 form 属性指向触发事件的元素所属的表单。以下元素拥有 form 属性:
<button>(当type属性为submit、reset或不设置时)<input>(当type属性为submit或image时)<output><fieldset>
特别注意的是,拥有form属性的表单元素可以写在form表单外面,使用form属性指向表单的id,这样点击该元素,相当于点击了form表单内部的submit按钮。
理解 e.currentTarget 上的 form 属性非常重要,因为通过它我们可以方便地访问到表单元素,即使触发事件的元素不是表单本身,而是表单内的按钮。
2. requestSubmit():触发表单提交的新方式
requestSubmit() 方法是 HTMLFormElement 的一个方法,用于以编程方式触发表单提交。与直接调用 form.submit() 不同,requestSubmit() 会触发表单的 submit 事件,从而可以执行表单校验等操作。这使得我们可以更灵活地控制表单提交流程,例如我们在textarea按下ctrl+回车可以直接提交表单。
3. JavaScript 表单校验:保证数据的有效性
表单校验是确保用户输入数据有效性的重要步骤。通过在客户端进行校验,可以减少服务器的负担,并及时向用户提供反馈。
function verifyForm(form, rules) {
const errors = {};
let isValid = true;
for (const fieldName in rules) {
const fieldValue = form.elements[fieldName]?.value?.trim();
const fieldRules = rules[fieldName];
// 清除错误提示
document.getElementById(`${fieldName}Error`)?.textContent = "";
for (const ruleName in fieldRules) {
const ruleValue = fieldRules[ruleName];
let errorMessage = null;
switch (ruleName) {
case 'required':
if (!fieldValue) {
errorMessage = ruleValue || `${fieldName} 是必填项`;
}
break;
case 'minLength':
if (fieldValue && fieldValue.length < ruleValue) { // 空值不进行长度校验
errorMessage = `${fieldName} 至少需要 ${ruleValue} 个字符`;
}
break;
case 'maxLength':
if (fieldValue && fieldValue.length > ruleValue) { // 空值不进行长度校验
errorMessage = `${fieldName} 最多允许 ${ruleValue} 个字符`;
}
break;
case 'pattern':
if (fieldValue && !ruleValue.test(fieldValue)) {// 空值不进行正则校验
errorMessage = ruleValue.message || `${fieldName} 格式不正确`;
}
break;
case 'custom':
if (typeof ruleValue === 'function' && !ruleValue(fieldValue)) {
errorMessage = `${fieldName} 不符合自定义规则`;
}
break;
}
if (errorMessage) {
errors[fieldName] = errors[fieldName] || [];
errors[fieldName].push(errorMessage);
isValid = false;
document.getElementById(`${fieldName}Error`).textContent = errorMessage; //在页面上显示错误信息
}
}
}
return { isValid, errors };
}
知名度表单验证js库:Validate.js
4. 无刷新提交:提升用户体验的关键
传统的 Form 提交如何导致页面刷新?
在传统的 HTML form 提交中,当用户点击 submit 按钮(<input type="submit"> 或 <button type="submit">),浏览器会执行以下步骤:
- 构建请求: 浏览器根据 form 元素的
action属性指定的 URL 和method属性指定的 HTTP 方法(GET 或 POST)构建一个 HTTP 请求。form 中的所有表单元素的值会被编码成请求体(POST 方法)或 URL 参数(GET 方法)。 - 发送请求: 浏览器向服务器发送构建好的 HTTP 请求。
- 服务器处理请求: 服务器接收到请求后,根据请求中的数据进行相应的处理(例如,将数据保存到数据库、执行某些计算等)。
- 服务器返回响应: 服务器处理完成后,会返回一个 HTTP 响应。这个响应通常包含一个新的 HTML 页面。
- 浏览器接收响应并渲染新页面: 浏览器接收到服务器的响应后,会解析响应体中的 HTML,并用新的页面内容替换当前页面,这就是我们看到的页面刷新。
为什么会刷新?
根本原因是传统的 form 提交是基于浏览器原生的提交机制,它依赖于服务器返回一个新的完整 HTML 页面。浏览器为了显示服务器返回的新内容,必须重新加载整个页面。
这种方式的缺点:
- 用户体验差: 页面刷新会导致短暂的白屏,打断用户的操作流程,影响用户体验。
- 浪费带宽: 每次提交都需要重新加载整个页面,浪费不必要的带宽。
- 状态丢失: 页面刷新会导致之前的页面状态丢失,例如表单中已填写的数据、滚动条的位置等。
5. 完整示例:融会贯通
将无刷新提交的讲解和完整示例合并在一起,以便更清晰地展示如何将所有知识点应用到实际代码中。
<!DOCTYPE html>
<html>
<head>
<title>优雅提交示例</title>
<style>
.error {
color: red;
margin-top: 5px;
font-size: small;
}
</style>
</head>
<body>
<form id="myForm">
<label for="username">用户名:</label>
<input type="text" id="username" name="username"><span class="error" id="usernameError"></span><br><br>
<label for="email">邮箱:</label>
<input type="email" id="email" name="email"><span class="error" id="emailError"></span><br><br>
<label for="age">年龄:</label>
<input type="number" id="age" name="age"><span class="error" id="ageError"></span><br><br>
<label for="desc">描述:</label><br>
<textarea id="desc" name="desc"></textarea><span class="error" id="descError"></span><br><br>
<button type="submit">提交</button>
</form>
<form id="myForm2">
<input type="text" id = 'input1'>
</form>
<button type="submit" form = 'myForm2'>外部提交</button>
<div id="result"></div>
<script>
// ... (verifyForm 函数,同上)
const myForm = document.getElementById('myForm');
const resultDiv = document.getElementById('result');
const descTextarea = document.getElementById('desc');
const btn = document.querySelector('[type="submit"][form]');
myForm.addEventListener('submit', function(event) {
event.preventDefault();
const rules = {
username: { required: "用户名不能为空", minLength: 3, maxLength: 10 },
email: { required: true, pattern: {test: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message:"请输入正确的邮箱格式"} },
age:{custom:(value)=>{
return !value || (value >= 18 && value <= 100)
}},
desc: {required: "描述不能为空"}
};
const validationResult = verifyForm(myForm, rules);
if (!validationResult.isValid) {
return; // 阻止提交
}
const formData = new FormData(myForm);
const submitButton = myForm.querySelector('[type="submit"]'); //找到提交按钮
submitButton.disabled = true; //禁用按钮,防止重复提交
resultDiv.textContent = '正在提交...';
fetch('/your-api-endpoint', { // 请替换为你的 API 端点
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
resultDiv.textContent = '提交成功!服务器返回:' + JSON.stringify(data);
myForm.reset();
submitButton.disabled = false; //恢复按钮可用
alert('提交成功')
})
.catch(error => {
submitButton.disabled = false; //恢复按钮可用
resultDiv.textContent = '提交失败:' + error;
console.error("请求错误:", error)
});
});
btn.addEventListener('click', function(e){
e.preventDefault();
//通过requestSubmit()方法触发表单的提交事件
e.target.form.requestSubmit()
})
descTextarea.addEventListener('keydown', (event) => {
if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {
event.preventDefault();
myForm.requestSubmit();
}
});
</script>
</body>
</html>
6. 一些优秀的表单库
-
React Hook Form: 专门为 React 应用设计的表单库,提供了强大的表单管理和验证功能。
-
Formik: 一个流行的表单库,可以与 React、Vue、Angular 等框架一起使用。
-
FormKit: 通过简化表单结构、生成、验证、主题化、提交、错误处理等,使开发人员能够以10倍的速度构建表单。
-
Yup: 一个用于对象 schema 验证的 JavaScript schema 构建器。它可以与 Formik 等表单库结合使用