Safari 11 XHR bug workaround

iOS 11.3 Safari / macOS Safari 11.1 empty <input type="file"> XHR bug workaround

by ypresto

HTML

<div id="main">
  <form>
    <div>
      Select File (leave this empty) <input type="file" name="file">  
    </div>
    <div>
      Text <input type="text" name="html" value="foobar">
    </div>
    <button id="submit-as-is-button" type="button">Submit as-is</button>
    <button id="submit-filtered-button" type="button">Submit w/ input filtered</button>
  </form>
  <div id="result">The result will be shown here.</div>
  <div id="echo">Request body will be shown here.</div>
</div>

CSS

body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#main {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  text-align: center;
  transition: all 0.2s;
  margin: 0 auto;
  width: 400px;
}

#main div {
  margin: 4px;
  padding: 4px;
  border: 1px solid #ccc;
}

#main #result {
  margin-top: 24px;
}

button {
  background: #0084ff;
  border: none;
  border-radius: 5px;
  padding: 8px 14px;
  font-size: 15px;
  color: #fff;
}

JavaScript

// iOS 11.3 Safari / macOS Safari 11.1 empty <input type="file"> XHR bug workaround.

var getFormData = function(form) {
  var $inputs = $('input[type="file"]:not([disabled])', form)
  $inputs.each(function(_, input) {
    if (input.files.length > 0) return
    $(input).prop('disabled', true)
  })
  var formData = new FormData(form)
  $inputs.prop('disabled', false)
  return formData
}

var submit = function(filter) {
  var $result = $('#result')
  var $echo = $('#echo')
  var formData = filter ? getFormData($('form')[0]) : new FormData($('form')[0])
  $result.text('In Progress')
	$.ajax({
    type: 'POST',
    enctype: 'multipart/form-data',
    url: '/echo/html/',
    data: formData,
    contentType: false,
    processData: false,
    cache: false,
    complete: function(xhr) {
      $result.text(xhr.status + ' ' + xhr.statusText)
      $echo.text("" + xhr.responseText)
    }
	})
}

$("#submit-as-is-button").on("click", function() { submit() })
$("#submit-filtered-button").on("click", function() { submit(true) })