如何校验所有表单字段填写完成后启用提交按钮,提交后禁用5秒
实现目标
实现表单提交按钮的状态控制逻辑:仅当所有输入框均填写有效值时,才enable提交按钮;点击提交后按钮保持disabled状态5秒,防止重复提交。
现有代码
Blade模板表单结构
<form method="POST" id="contactForm"> <div class="row"> <div class="col-6"> <input type="text" name="name" id="name" value=""/> <div class="error">Error Message</div> </div> <div class="col-6"> <input type="text" name="email" id="email" value=""/> <div class="error">Error Message</div> </div> <div class="col-12"> <textarea name="body" id="message" rows="5"> Enter your message</textarea> <div class="error">Error message</div> </div> <div class="col-12"> <input type="submit" value="Submit" class="primary" id="buttonSubmit" disabled/> </div> </div> </form>
后端Controller校验逻辑
后端已经添加必填规则校验:
public function store() { $data = request()->validate([ 'name' => 'required', 'email' => 'required|email', 'body' => 'required', ]); }
现有前端控制脚本
const button = document.querySelector("#buttonSubmit"); const buttonExpirationDataKey = 'button-disabled-expiration'; button.addEventListener("click", () => { var form = document.getElementById("contactForm"); var fields = ["name", "email","body"]; var i, l = fields.length; var fieldname; for (i = 0; i < l; i++) { fieldname = fields[i]; if(form[fieldname].value !== ""){ button.disabled = true; let now = new Date(); let expirationTime = 1000 * 5; // 5 secs to disable the submit button let expirationDate = new Date(now.getTime() + expirationTime); localStorage.setItem(buttonExpirationDataKey, expirationDate); button.dataset.enabledAt = expirationDate; } else { button.disabled = false; } return false; } });
现存问题
当前代码存在5个核心错误:
- 提交按钮初始状态为
disabled,浏览器默认不会响应disabled元素的点击事件,原代码把所有状态判断逻辑都绑定在按钮的click事件上,逻辑从根源上无法正常执行 - for循环内部第一趟遍历就执行了
return false,直接终止循环,根本不会校验后续字段 - 按钮状态判断逻辑完全写反:只要单个字段非空就禁用按钮,遇到空字段就启用按钮,和需求逻辑完全颠倒
- 没有绑定输入事件做实时校验,用户填写内容时按钮状态不会同步更新
- 提交后的5秒禁用没有配套的定时恢复逻辑,禁用后不会自动变回可用状态,也没有处理页面刷新后冷却期的状态保持
- textarea默认值带前置空格,会干扰空值判断,即使用户没有输入内容,value也不是空字符串
修正方案
核心调整逻辑:
- 把表单校验逻辑抽成独立公共函数,统一判断所有字段的有效性(含邮箱格式校验、空值trim处理)
- 给所有输入字段绑定
input事件,用户输入时实时更新按钮状态 - 页面加载时自动检查是否处于提交后的5秒冷却期,冷却期内保持按钮禁用,到点自动恢复
- 提交事件中先做全量校验,校验通过才执行提交、触发5秒禁用逻辑
- 修正textarea默认值的前置空格问题,避免非用户输入的内容干扰空值判断
修正后的完整JS代码如下:
const button = document.querySelector("#buttonSubmit"); const form = document.getElementById("contactForm"); const buttonExpirationDataKey = 'button-disabled-expiration'; const fields = ["name", "email", "body"]; // 邮箱格式校验正则 const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; // 全字段校验函数,返回是否所有字段均有效 function checkFormValid() { // 先判断是否处于提交后的禁用冷却期 const expiration = localStorage.getItem(buttonExpirationDataKey); if (expiration && new Date(expiration).getTime() > Date.now()) { return false; } // 逐字段校验 for (const field of fields) { const val = form[field].value.trim(); // 空值直接判定无效 if (!val) return false; // 邮箱字段额外校验格式 if (field === 'email' && !emailReg.test(val)) return false; } return true; } // 更新按钮状态 function updateButtonStatus() { button.disabled = !checkFormValid(); } // 所有输入项绑定实时校验事件 fields.forEach(field => { form[field].addEventListener('input', updateButtonStatus); }); // 页面加载时初始化状态 window.addEventListener('load', () => { updateButtonStatus(); const expiration = localStorage.getItem(buttonExpirationDataKey); if (expiration) { const remainTime = new Date(expiration).getTime() - Date.now(); if (remainTime > 0) { // 剩余冷却时间内保持禁用,到点自动恢复 setTimeout(() => { localStorage.removeItem(buttonExpirationDataKey); updateButtonStatus(); }, remainTime); } else { localStorage.removeItem(buttonExpirationDataKey); } } }); // 表单提交事件逻辑 form.addEventListener("submit", (e) => { // 提交前再做一次校验,不通过直接拦截提交 if (!checkFormValid()) { e.preventDefault(); return; } // 校验通过,设置5秒禁用 button.disabled = true; const expirationTime = 5 * 1000; const expirationDate = new Date(Date.now() + expirationTime); localStorage.setItem(buttonExpirationDataKey, expirationDate); // 定时器到点恢复按钮状态 setTimeout(() => { localStorage.removeItem(buttonExpirationDataKey); updateButtonStatus(); }, expirationTime); });
建议将textarea的默认值修改为不带前置空格的形式,或直接使用
placeholder属性做输入提示,避免默认内容干扰校验:<textarea name="body" id="message" rows="5" placeholder="Enter your message"></textarea>
内容的提问来源于stack exchange,提问作者CuteSako
相关产品推荐
相关产品推荐

