I'm trying to submit a form via Ajax with JavaScript. It works on IE, Firefox, Chrome and iOS in Browserstack, but it doesn't work in iOS Simulator or a real iOS device or desktop Safari.
I've tried XMLHttpRequest onload
and XMLHttpRequest onreadystatechange
, neither gets triggered. As far as I can tell request.open('POST', url, true);
isn't working. console.log('2');
never gets triggered either.
I added this for debugging:
request.send(data);
console.log(request);
setTimeout(function(){ console.log(request); }, 3000);
They both return readyState: 1 in Safari, in other browsers the first one returns readyState: 1 and the second one returns readyState: 4 as expected.
Here's my code pared down to the essentials:
$('.ajax-form').submit(function(event) {
var form = event.target;
var data = new FormData(form);
var request = new XMLHttpRequest();
var method = form.getAttribute("method");
var action = form.getAttribute("action");
var url = window.location.href
request.open('POST', url, true);
request.setRequestHeader("X-Requested-With", "XMLHttpRequest");
request.setRequestHeader("HTTP_X_REQUESTED_WITH", "XMLHttpRequest");
request.setRequestHeader("pragma", "no-cache");
console.log('1');
request.onreadystatechange = function() {
console.log('2');
if (request.readyState === 4 && request.status === 200) {
var response = JSON.parse(request.response);
if (response.success && response.finished) {
// Go to selected success page
window.location.href = $(form).find('[name="formReturnUrl"]').attr('value');
} else if (response.errors || response.formErrors) {
// Deal with errors
}
}
};
request.send(data);
console.log(request);
setTimeout(function() {
console.log(request);
}, 3000);
event.preventDefault();
});
All my iOS testing is on iOS 11 in Safari. I've read a few things about iOS being weird, but I haven't found a solution that works for me. What am I doing wrong?
I'm trying to submit a form via Ajax with JavaScript. It works on IE, Firefox, Chrome and iOS in Browserstack, but it doesn't work in iOS Simulator or a real iOS device or desktop Safari.
I've tried XMLHttpRequest onload
and XMLHttpRequest onreadystatechange
, neither gets triggered. As far as I can tell request.open('POST', url, true);
isn't working. console.log('2');
never gets triggered either.
I added this for debugging:
request.send(data);
console.log(request);
setTimeout(function(){ console.log(request); }, 3000);
They both return readyState: 1 in Safari, in other browsers the first one returns readyState: 1 and the second one returns readyState: 4 as expected.
Here's my code pared down to the essentials:
$('.ajax-form').submit(function(event) {
var form = event.target;
var data = new FormData(form);
var request = new XMLHttpRequest();
var method = form.getAttribute("method");
var action = form.getAttribute("action");
var url = window.location.href
request.open('POST', url, true);
request.setRequestHeader("X-Requested-With", "XMLHttpRequest");
request.setRequestHeader("HTTP_X_REQUESTED_WITH", "XMLHttpRequest");
request.setRequestHeader("pragma", "no-cache");
console.log('1');
request.onreadystatechange = function() {
console.log('2');
if (request.readyState === 4 && request.status === 200) {
var response = JSON.parse(request.response);
if (response.success && response.finished) {
// Go to selected success page
window.location.href = $(form).find('[name="formReturnUrl"]').attr('value');
} else if (response.errors || response.formErrors) {
// Deal with errors
}
}
};
request.send(data);
console.log(request);
setTimeout(function() {
console.log(request);
}, 3000);
event.preventDefault();
});
All my iOS testing is on iOS 11 in Safari. I've read a few things about iOS being weird, but I haven't found a solution that works for me. What am I doing wrong?
Share Improve this question edited Jul 6, 2018 at 6:24 user7637745 9852 gold badges14 silver badges27 bronze badges asked Jul 5, 2018 at 23:10 Simon KuranSimon Kuran 2552 silver badges11 bronze badges 2-
I don't know why this is happening but I remend trying
$.post()
, since you're already using jQuery. – user5734311 Commented Jul 5, 2018 at 23:27 - Thanks for the idea, but jQuery Ajax isn't correctly returning form error data from my CMS (Craft running Freeform). I'm trying to use pure JavaScript for the Ajax since that works perfectly in my environment, except in iOS Safari... Any other ideas? – Simon Kuran Commented Jul 5, 2018 at 23:59
2 Answers
Reset to default 8Solved. Thanks to @mxcoder for pointing me in the right direction.
Safari (desktop and iOS) have a bug when creating FormData with empty file fields. I solved this by disabling empty file fields right before creating FormData and then re-enabling them right after so the form can be reused after the Ajax is submitted.
I changed var data = new FormData(form);
to this:
// Disable empty file fields before submitting.
if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {
var $inputs = $('input[type="file"]:not([disabled])', form);
$inputs.each(function(_, input) {
if (input.files.length > 0) return
$(input).prop('disabled', true);
});
}
var data = new FormData(form);
// Re-enable empty file fields after creating FormData.
if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {
$inputs.prop('disabled', false);
}
- If the browser is Safari, disable empty
<input type="file">
elements - Create FormData
- If the browser is Safari, re-enable empty
<input type="file">
elements. This allows the form to be reused after successful submission or after correcting errors.
Interesting problem, Safari is indeed tricky sometimes.
I don't have a solution yet for you but can you try the following:
- Try not using FormData, maybe iOS doesn't play nicely with XHR + FormData yet, it could be a bug worthy to report!
- Can you share the request and response body and headers? Maybe there's something in the server side failing for the iOS request, again, maybe some misplaced header.