Gatsby生产构建后AJAX表单请求失效,提交自动跳转求助
我帮你排查了几个核心问题点,结合你的代码给出针对性的修复方案:
1. 表单提交事件绑定逻辑错误(最关键原因)
你现在是给提交按钮绑定click事件,但表单的触发场景不止点击按钮(比如用户在输入框按回车提交),更重要的是在Gatsby生产构建的hydration(水化)过程中,按钮的click事件绑定可能存在延迟或失效,导致event.preventDefault()没被正确执行,最终触发表单的默认跳转行为。
修复方案:改为监听表单的submit事件,这样能覆盖所有提交场景,且阻止默认行为的逻辑更可靠:
if (typeof window !== `undefined`) { document.addEventListener('DOMContentLoaded',function(){ var contactForm = document.querySelector('#myform'), inputName = contactForm.querySelector('[name="name"]'), inputEmail = contactForm.querySelector('[name="email"]'), inputMessage = contactForm.querySelector('[name="message"]'), sendButton = contactForm.querySelector('#send-button'); // 替换为监听表单的submit事件 contactForm.addEventListener('submit', function(event){ event.preventDefault(); // 彻底阻止表单默认提交跳转 sendButton.value = 'Sending..'; var xhr = new XMLHttpRequest(); // 改成HTTPS协议,避免生产环境混合内容被浏览器拦截 xhr.open('POST', 'https://formspree.io/testmail@gmail.com', true); xhr.setRequestHeader("Accept", "application/json"); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.onloadend = function (res) { if (res.target.status === 200){ sendButton.value = 'Message sent! I will be in touch soon.'; contactForm.reset(); } else { sendButton.value = 'Error!'; } } // 修复参数格式:添加&分隔符,并用encodeURIComponent处理特殊字符 var formData = "name=" + encodeURIComponent(inputName.value) + "&email=" + encodeURIComponent(inputEmail.value) + "&message=" + encodeURIComponent(inputMessage.value); xhr.send(formData); }); }); }
2. AJAX请求参数格式错误
你的原始代码里,xhr.send()的参数没有用&分隔,会导致所有参数被合并成一串无效内容(比如name=张三email=xxx@xx.commessage=xxx),Formspree无法解析这些参数,请求失败后就会触发表单的默认跳转。同时没有对输入值做编码处理,遇到空格、@、&等特殊字符也会导致请求异常。
上面的修复代码已经解决了这个问题:添加了&分隔符,并通过encodeURIComponent对用户输入做了编码。
3. 混合内容协议不兼容
你的表单action和AJAX请求用的是http://formspree.io,如果生产站点部署在HTTPS环境下,浏览器会阻止HTTP的AJAX请求(混合内容安全限制),导致请求失败,最终触发表单默认跳转。
修复方案:把请求地址改成https://formspree.io/testmail@gmail.com,和站点协议保持一致。
额外优化建议(如果是React组件场景)
如果你的表单是在React组件中实现,更推荐用React的状态管理和useEffect来绑定事件,避免原生DOM操作的兼容性问题:
import { useEffect, useRef } from 'react'; const ContactForm = () => { const formRef = useRef(null); const sendButtonRef = useRef(null); useEffect(() => { const contactForm = formRef.current; const sendButton = sendButtonRef.current; const handleSubmit = (e) => { e.preventDefault(); sendButton.value = 'Sending..'; const inputName = contactForm.querySelector('[name="name"]'); const inputEmail = contactForm.querySelector('[name="email"]'); const inputMessage = contactForm.querySelector('[name="message"]'); const xhr = new XMLHttpRequest(); xhr.open('POST', 'https://formspree.io/testmail@gmail.com', true); xhr.setRequestHeader("Accept", "application/json"); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.onloadend = (res) => { if (res.target.status === 200){ sendButton.value = 'Message sent! I will be in touch soon.'; contactForm.reset(); } else { sendButton.value = 'Error!'; } } const formData = "name=" + encodeURIComponent(inputName.value) + "&email=" + encodeURIComponent(inputEmail.value) + "&message=" + encodeURIComponent(inputMessage.value); xhr.send(formData); }; contactForm.addEventListener('submit', handleSubmit); // 组件卸载时移除事件监听,避免内存泄漏 return () => contactForm.removeEventListener('submit', handleSubmit); }, []); return ( <form ref={formRef} id='myform' action="https://formspree.io/testmail@gmail.com" method="POST"> <div className="formgroup"> <input id='name' type="text" name="name" placeholder='Name'/> <input id='email' type="email" name="email" placeholder='Email'/> </div> <div className="formfield"> <textarea id='message' name="message" rows='6' placeholder='Message'/> </div> <div className="formfield"> <input ref={sendButtonRef} id='send-button' type="submit" value="Send"/> </div> </form> ); }; export default ContactForm;
内容的提问来源于stack exchange,提问作者Martin Conde

