集成reCAPTCHA的表单仅Firefox提交失败问题排查
根本原因
和CSP策略、Firefox安全等级没有关系,问题出在HTML末尾引入的公共reCAPTCHA提交脚本的逻辑缺陷,触发了不同浏览器的事件循环行为差异:
- 表单校验通过进入reCAPTCHA执行分支时,你没有调用
e.preventDefault()阻止浏览器默认的表单提交行为。Chrome、Edge等Chromium内核浏览器的事件队列调度逻辑会优先等待微任务执行,很多场景下会等grecaptcha异步请求返回后才触发原生提交,所以看起来功能正常;但Firefox会在submit监听器的同步代码执行完毕后立刻触发原生表单提交,直接中断还在等待响应的reCAPTCHA异步请求,既拿不到验证token,也走不到你写的赋值后提交逻辑,最终表现为表单提交失败、无响应。 - 代码里还附带两个不区分浏览器的逻辑bug:一是提交时的合法性判断漏了隐私政策勾选框的校验,二是异步回调里用
e.target.submit()获取表单对象不可靠,Firefox下事件target可能指向触发提交的按钮元素而非form本身,调用submit会直接报错。
修复方案
直接替换公共include_recaptcha.html里的提交脚本即可,对应修复所有问题:
- 进入submit监听后第一时间阻止默认提交行为,完全由JS控制提交流程,避免浏览器原生提交打断异步逻辑
- 补全隐私政策勾选框的校验判断,避免漏校验
- 提前缓存当前表单的DOM引用,异步回调里直接调用缓存的表单对象的submit方法,不依赖事件target的指向
修复后的脚本代码如下:
var rcf = document.getElementsByName('g-recaptcha-response'); for(var i=0; i<rcf.length; i++) { const currentForm = rcf[i].form; // 提前获取当前表单的字段引用,避免依赖页面全局变量 const navn = currentForm.querySelector('#navn'); const email = currentForm.querySelector('#mail'); const telefon = currentForm.querySelector('#telefon'); const message = currentForm.querySelector('#message'); const personvernCheck = currentForm.querySelector('#checkbox'); currentForm.addEventListener('submit', function(e) { // 永远先阻止默认提交,完全接管提交流程 e.preventDefault(); // 全字段校验 if (!navn.validity.valid || !email.validity.valid || !telefon.validity.valid || !message.validity.valid || !personvernCheck.checked) { makeRed(); divFill(); return; } // 校验通过再执行reCAPTCHA验证 grecaptcha.execute('{sitekey}', {action: 'homepage'}) .then(function(token) { document.getElementById('recapta').value = token; // 直接调用缓存的表单对象提交,不依赖e.target指向 currentForm.submit(); }); }); }
额外提一句:你前面写的showError函数有个小问题,函数末尾给四个错误提示元素加kontaktactive类的代码是写在函数外面的,页面首次加载时就会给错误提示加上激活样式,建议把这几行移到showError函数内部,避免初始状态样式异常。
内容的提问来源于stack exchange,提问作者DOFFIZ
相关产品推荐
相关产品推荐

