AJAX 联系表单(前端验证 + 异步提交)
后端对接
来源:16idc
一个完整的 AJAX 联系表单组件,包含前端输入验证、实时错误提示、异步提交和成功/错误反馈。
HTML
<form class="contact-form" id="contactForm" novalidate>
<div class="form-group">
<label for="name">姓名 *</label>
<input type="text" id="name" name="name" required minlength="2"
placeholder="请输入您的姓名">
<span class="error-message" id="nameError"></span>
</div>
<div class="form-group">
<label for="email">邮箱 *</label>
<input type="email" id="email" name="email" required
placeholder="请输入您的邮箱">
<span class="error-message" id="emailError"></span>
</div>
<div class="form-group">
<label for="message">留言 *</label>
<textarea id="message" name="message" required minlength="10"
rows="5" placeholder="请输入留言内容(至少 10 个字符)"></textarea>
<span class="error-message" id="messageError"></span>
</div>
<!-- 蜜罐字段 -->
<input type="text" name="website" class="honeypot" tabindex="-1" autocomplete="off">
<button type="submit" class="submit-btn" id="submitBtn">
<span class="btn-text">发送留言</span>
<span class="btn-loading" style="display:none">发送中...</span>
</button>
</form>
<div id="formSuccess" class="form-success" style="display:none">
<h3>✓ 感谢您的留言!</h3>
<p>我们会尽快回复您。</p>
</div>
CSS
.contact-form {
max-width: 600px;
margin: 0 auto;
padding: 32px;
background: #f9fafb;
border-radius: 12px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 6px;
font-weight: 500;
color: #374151;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 10px 14px;
border: 1px solid #d1d5db;
border-radius: 8px;
font-size: 16px;
transition: border-color .2s;
box-sizing: border-box;
}
.form-group input:focus,
.form-group textarea:focus {
outline: none;
border-color: #4F46E5;
box-shadow: 0 0 0 3px rgba(79,70,229,.1);
}
.form-group input.error,
.form-group textarea.error {
border-color: #ef4444;
}
.error-message {
display: block;
margin-top: 4px;
font-size: 14px;
color: #ef4444;
min-height: 20px;
}
.honeypot {
display: none !important;
}
.submit-btn {
width: 100%;
padding: 12px 24px;
background: #4F46E5;
color: #fff;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: background .2s;
}
.submit-btn:hover { background: #4338CA; }
.submit-btn:disabled { opacity: .6; cursor: not-allowed; }
.form-success {
text-align: center;
padding: 32px;
background: #f0fdf4;
border-radius: 12px;
color: #166534;
}
JavaScript
document.addEventListener('DOMContentLoaded', () => {
const form = document.getElementById('contactForm');
const submitBtn = document.getElementById('submitBtn');
const btnText = submitBtn.querySelector('.btn-text');
const btnLoading = submitBtn.querySelector('.btn-loading');
// 实时验证
const fields = ['name', 'email', 'message'];
fields.forEach(field => {
const input = document.getElementById(field);
input.addEventListener('blur', () => validateField(field));
input.addEventListener('input', () => {
const error = document.getElementById(field + 'Error');
if (error.textContent) validateField(field);
});
});
function validateField(field) {
const input = document.getElementById(field);
const error = document.getElementById(field + 'Error');
let message = '';
switch (field) {
case 'name':
if (!input.value.trim()) message = '请输入姓名';
else if (input.value.trim().length < 2) message = '姓名至少 2 个字符';
break;
case 'email':
if (!input.value.trim()) message = '请输入邮箱';
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input.value))
message = '邮箱格式不正确';
break;
case 'message':
if (!input.value.trim()) message = '请输入留言';
else if (input.value.trim().length < 10) message = '留言至少 10 个字符';
break;
}
error.textContent = message;
input.classList.toggle('error', !!message);
return !message;
}
// 提交表单
form.addEventListener('submit', async (e) => {
e.preventDefault();
// 验证所有字段
const valid = fields.every(validateField);
if (!valid) return;
// 显示加载状态
submitBtn.disabled = true;
btnText.style.display = 'none';
btnLoading.style.display = 'inline';
try {
const formData = new FormData(form);
const data = Object.fromEntries(formData);
const response = await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const result = await response.json();
if (response.ok) {
form.style.display = 'none';
document.getElementById('formSuccess').style.display = 'block';
} else {
alert(result.error || '提交失败,请稍后重试');
}
} catch (error) {
alert('网络错误,请检查连接后重试');
} finally {
submitBtn.disabled = false;
btnText.style.display = 'inline';
btnLoading.style.display = 'none';
}
});
});
功能特性
- ✅ 实时输入验证(失焦 + 输入时)
- ✅ 异步提交(无页面刷新)
- ✅ 加载状态(按钮禁用 + 加载动画)
- ✅ 错误提示(字段级别 + 全局)
- ✅ 成功反馈(替换表单为成功消息)
- ✅ 蜜罐反垃圾保护
- ✅ 配合后端 PHP/Node.js/Flask 使用