Bootstrap表单验证与自定义提交逻辑冲突问题及优化咨询
问题描述
我尝试创建一个用于POST新用户的表单,但遇到了问题。我使用Bootstrap的浏览器默认表单验证,按钮代码如下:
<button class="btn btn-primary btn-addUser" type="submit">Add User</button>
这个按钮同时绑定了从输入框获取数据并创建新POST的函数:
const addUsersBtn = document.querySelector(".btn-addUser"); // 绑定添加用户按钮的点击事件 addUsersBtn.addEventListener("click", postNewUser); // 提交新用户的函数 function postNewUser(e) { e.preventDefault(); // 获取表单输入值 const name = form.elements.namedItem("name").value; const username = form.elements.namedItem("username").value; const email = form.elements.namedItem("email").value; const phone = form.elements.namedItem("phone").value; const website = form.elements.namedItem("website").value; // 构造用户对象 const newPost = { name: name, username: username, email: email, phone: phone, website: website, user_id: `id_${Math.random() * Math.random()}`, }; // 创建POST请求 createPost(newPost, (response) => { const card = newPostTemplate(response); container.insertAdjacentElement("afterbegin", card); form.reset(); initPopovers(); }); }
但调用postNewUser函数时,表单验证失效;移除按钮的btn-addUser类后,验证恢复正常。我的addUser.js完整代码如下:
// 获取表单元素 const form = document.forms["addUser"]; // 创建POST请求的函数 function createPost(body, callback) { return new Promise((resolve, reject) => { const request = new XMLHttpRequest(); request.open("POST", "https://jsonplaceholder.typicode.com/posts"); request.addEventListener("load", () => { const response = JSON.parse(request.responseText); if (request.status >= 400) { reject(request.response); } else { resolve(request.response); } callback(response); }); request.setRequestHeader("Content-type", "application/json; charset=UTF-8"); request.send(JSON.stringify(body)); }); } // 生成用户卡片的模板函数 function newPostTemplate(post) { const card = document.createElement("div"); card.classList.add("card"); const cardBody = document.createElement("div"); cardBody.classList.add("card-body"); const userBtn = document.createElement("button"); userBtn.setAttribute("type", "button"); userBtn.classList.add("btn", "btn-info", "btn-lg"); userBtn.setAttribute("data-bs-toggle", "popover"); userBtn.setAttribute("data-bs-html", "true"); userBtn.setAttribute( "data-bs-title", "用户附加信息:" ); userBtn.setAttribute( "data-bs-content", `用户名:${post.username}<br/>邮箱:${post.email}<br/>电话:${post.phone}<br/> 网站:${post.website}` ); const title = document.createElement("h5"); title.classList.add("card-title"); title.textContent = post.name; userBtn.appendChild(title); cardBody.appendChild(userBtn); card.appendChild(cardBody); return card; } // 绑定添加用户按钮的点击事件 addUsersBtn.addEventListener("click", postNewUser); // 提交新用户的函数 function postNewUser(e) { e.preventDefault(); // 获取表单输入值 const name = form.elements.namedItem("name").value; const username = form.elements.namedItem("username").value; const email = form.elements.namedItem("email").value; const phone = form.elements.namedItem("phone").value; const website = form.elements.namedItem("website").value; // 构造用户对象 const newPost = { name: name, username: username, email: email, phone: phone, website: website, user_id: `id_${Math.random() * Math.random()}`, }; // 创建POST请求 createPost(newPost, (response) => { const card = newPostTemplate(response); container.insertAdjacentElement("afterbegin", card); form.reset(); initPopovers(); }); }
我有两个问题:
- 是否可以设置两个按钮:第一个不带
btn-addUser类,第二个带btn-addUser类?我想给后者加上Bootstrap的禁用状态,当第一个按钮通过表单验证后解除禁用;点击带btn-addUser类的按钮后再将其禁用。但我不知道怎么获取验证通过的反馈。 - 有没有更简洁的实现方式?
解决方案
问题1的实现方案
完全可以用两个按钮实现需求,核心是利用HTML5表单的checkValidity()方法和输入事件监听验证状态:
步骤1:修改HTML按钮结构
<!-- 隐藏默认提交按钮,仅用于触发验证逻辑 --> <button type="submit" class="d-none"></button> <!-- 实际操作按钮,默认禁用 --> <button class="btn btn-primary btn-addUser" type="button" disabled>添加用户</button>
步骤2:监听表单验证状态
在addUser.js中添加实时检查逻辑:
const form = document.forms["addUser"]; const addUserBtn = document.querySelector(".btn-addUser"); // 检查表单有效性并更新按钮状态 function checkFormValidity() { addUserBtn.disabled = !form.checkValidity(); } // 给所有表单控件绑定输入/变化事件,实时验证 Array.from(form.elements).forEach(element => { element.addEventListener("input", checkFormValidity); element.addEventListener("change", checkFormValidity); }); // 页面加载时先执行一次检查 checkFormValidity();
步骤3:修改按钮点击逻辑
点击操作按钮时,先手动触发验证,再执行提交:
addUserBtn.addEventListener("click", async () => { // 手动触发验证,显示错误提示 form.reportValidity(); if (!form.checkValidity()) return; // 禁用按钮防止重复提交 addUserBtn.disabled = true; try { // 快速获取表单数据 const formData = new FormData(form); const newPost = { ...Object.fromEntries(formData), user_id: `id_${crypto.randomUUID()}` // 用规范UUID替代随机数 }; // 发送请求并处理结果 const response = await createPost(newPost); const card = newPostTemplate(response); container.insertAdjacentElement("afterbegin", card); form.reset(); initPopovers(); } catch (error) { console.error("提交失败:", error); // 提交失败后恢复按钮状态 addUserBtn.disabled = false; } });
问题2:更简洁的实现方式
可以从三个核心方向简化代码:
1. 用Fetch API替代XMLHttpRequest
Fetch是现代浏览器原生API,语法更简洁:
async function createPost(body) { const response = await fetch("https://jsonplaceholder.typicode.com/posts", { method: "POST", headers: { "Content-Type": "application/json; charset=UTF-8" }, body: JSON.stringify(body) }); if (!response.ok) throw new Error(`HTTP错误:${response.status}`); return response.json(); }
2. 简化表单数据获取
用FormData和Object.fromEntries快速提取所有表单值:
const formData = new FormData(form); const newPost = { ...Object.fromEntries(formData), user_id: crypto.randomUUID() };
3. 直接监听表单submit事件
不需要额外按钮,直接绑定表单的submit事件,原生验证会自动触发:
form.addEventListener("submit", async (e) => { e.preventDefault(); const submitBtn = e.target.querySelector(".btn-addUser"); submitBtn.disabled = true; try { const formData = new FormData(form); const newPost = { ...Object.fromEntries(formData), user_id: crypto.randomUUID() }; const response = await createPost(newPost); const card = newPostTemplate(response); container.insertAdjacentElement("afterbegin", card); form.reset(); initPopovers(); } catch (error) { console.error("提交失败:", error); submitBtn.disabled = false; } });
对应的按钮HTML只需保留一个:
<button class="btn btn-primary btn-addUser" type="submit">添加用户</button>
这种方式下,点击按钮时原生验证会自动执行,验证不通过直接显示提示,验证通过才会进入自定义提交逻辑,完全解决了原有的验证失效问题。
内容的提问来源于stack exchange,提问作者Yaroslav Demchenko
相关产品推荐
相关产品推荐

