移动端表单提交异常排查:iOS未使用上传字段时表单卡顿问题
解决iOS设备上未上传文件时表单提交卡住的问题
你遇到的是iOS Safari特有的FormData处理bug——当表单包含未选择文件的<input type="file">字段时,构造的FormData会导致AJAX请求异常超时,最终表现为提交按钮一直显示"Sending ..."且页面无响应。下面给出两种可靠的解决方案,以及优化建议:
方案一:临时移除空文件输入(简单直接)
在生成FormData之前,先把表单中没有选择文件的文件输入临时移除,提交完成后再恢复它们,这样就能绕过iOS的bug:
$('#reused_form').submit(function(e) { e.preventDefault(); $form = $(this); // 1. 临时移除未选择文件的<input type="file"> const emptyFileInputs = $form.find('input[type="file"]').filter(function() { return this.files.length === 0; }).detach(); // 2. 更新按钮状态 $('button[type="submit"]', $form).each(function() { const $btn = $(this); $btn.prop('type','button'); $btn.prop('orig_label', $btn.text()); $btn.text('Sending ...'); }); // 3. 构造FormData(此时已排除空文件输入) const formdata = new FormData(this); // 4. 把临时移除的输入元素加回表单 emptyFileInputs.appendTo($form); // 5. 发送AJAX请求,新增超时和错误处理 $.ajax({ type: "POST", url: 'handler.php', data: formdata, success: after_form_submitted, dataType: 'json', processData: false, contentType: false, cache: false, timeout: 10000, // 设置10秒超时 error: function(xhr, status, error) { // 出错时恢复按钮状态 $('button[type="button"]', $form).each(function() { const $btn = $(this); const label = $btn.prop('orig_label'); if (label) { $btn.prop('type','submit'); $btn.text(label); $btn.prop('orig_label',''); } }); // 给用户友好提示 $('#error_message').html('<ul><li>提交超时或失败,请重试</li></ul>').show(); $('#success_message').hide(); } }); });
方案二:手动构建FormData(更灵活)
如果不想操作DOM元素,可以手动遍历表单字段,只把有值的字段(包括已选择的文件)加入FormData:
$('#reused_form').submit(function(e) { e.preventDefault(); $form = $(this); // 更新按钮状态 $('button[type="submit"]', $form).each(function() { const $btn = $(this); $btn.prop('type','button'); $btn.prop('orig_label', $btn.text()); $btn.text('Sending ...'); }); // 手动构造FormData const formdata = new FormData(); // 添加非文件字段 $form.find(':input:not([type="file"])').each(function() { // 排除按钮和禁用字段 if (!$(this).is(':button, :disabled') && this.name) { formdata.append(this.name, $(this).val()); } }); // 添加已选择文件的字段(支持多文件上传) $form.find('input[type="file"]').each(function() { if (this.files.length > 0 && this.name) { for (let i = 0; i < this.files.length; i++) { formdata.append(this.name + '[]', this.files[i]); } } }); // 发送AJAX请求(同样加上超时和错误处理) $.ajax({ type: "POST", url: 'handler.php', data: formdata, success: after_form_submitted, dataType: 'json', processData: false, contentType: false, cache: false, timeout: 10000, error: function(xhr, status, error) { // 恢复按钮状态+提示用户 $('button[type="button"]', $form).each(function() { const $btn = $(this); const label = $btn.prop('orig_label'); if (label) { $btn.prop('type','submit'); $btn.text(label); $btn.prop('orig_label',''); } }); $('#error_message').html('<ul><li>提交超时或失败,请重试</li></ul>').show(); $('#success_message').hide(); } }); });
关键优化说明
- 超时处理:新增
timeout参数,避免请求无限期挂起; - 错误回调:在请求失败/超时时恢复按钮状态,同时给用户明确提示,提升体验;
- 字段过滤:两种方案都排除了空文件字段,从根源上避免iOS的FormData bug。
内容的提问来源于stack exchange,提问作者salsaverde




