Internet Explorer 11 strips empty <input> elements on POST if Javascript used against them in DOM first

My question is simple even if the problem description takes up the rest of this post – is the IE behavior described below a known bug, by design, or just “one of those things” we have to know and code around?

Edit: Or is it all in my own environment for some reason, per @James comment?

Problem Statement

When there are multiple <input type=’file’> elements on a form, if some of the input elements are left empty and no Javascript runs against the input elements, each element is sent back in a POST as part of delimited multipart form data, including the empty elements, in the order displayed on the page. This is expected behavior.

If there is Javascript that interacts with the input elements (e.g., validating them), Chrome and Firefox both still send back all the elements, whether they have values or not. This is also expected behavior. However, unless the empty input elements are “reset” after merely reading them in the DOM with Javascript, IE 11 will only send back the input elements that actually have values.

Here is a summarization of the number of multipart form elements sent in each scenario, by browser:

Browser |   No    | JS, no  |   JS,
        |   JS    | "reset" | "reset"
--------+---------+---------+---------
IE 11   |    4    |    2    |    4
Firefox |    4    |    4    |    4
Chrome  |    4    |    4    |    4

Sample Code

Consider the following 100% pure HTML and Javascript – all client-side, with no CSS, jQuery or other fillers. The things to note:

  • The fields are wrapped in a form with three submit buttons (for our three test cases) that all do a POST back to the server.
  • The three submit buttons allow:
    • Submitting the form with no Javascript involved.
    • Submitting the form with Javascript iterating the input elements but not doing anything to them (which still triggers the bug interesting behavior in IE – I stripped it down to this very simple case, but it started in code much more complex than shown).
    • Submitting the form with Javascript that “resets” the empty input elements, which causes the empty ones to flow from IE on POST again.
  • There are four <input type=’file’> elements to allow us to leave some empty.

This is not optimal code, but I think it shows what I am talking about as easily as possible.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Page 1</title>
</head>
<body>
    <form action="/Default/Details/1" enctype="multipart/form-data" id="fileForm" method="post">
        <dl>
            <dt><label for="fileinput1">File input 1</label></dt>
            <dd>
                <input type="file" name="files" id="fileinput1" />
            </dd>
        </dl>
        <dl>
            <dt><label for="fileinput2">File input 2</label></dt>
            <dd>
                <input type="file" name="files" id="fileinput2" />
            </dd>
        </dl>
        <dl>
            <dt><label for="fileinput3">File input 3</label></dt>
            <dd>
                <input type="file" name="files" id="fileinput3" />
            </dd>
        </dl>
        <dl>
            <dt><label for="fileinput4">File input 4</label></dt>
            <dd>
                <input type="file" name="files" id="fileinput4" />
            </dd>
        </dl>
        <div class="col-md-offset-1 col-md-10">
            <input id="submitButton" type="submit" value="No Javascript, no worries" />
            <input id="submitButton" type="submit" value="IE strips empty fields" onclick="ValidateForm();" />
            <input id="submitButton" type="submit" value="We put them back" onclick="ValidateForm2();" />
        </div>
        <script>
            function ValidateForm() {
                var inputs = document.getElementsByTagName('input');

                for (var i = 0; i < inputs.length; i++) {
                    // For each file input, check to see if it has a file (each input element can select multiple files,
                    // only look for first file in each input element).
                    if (inputs[i]['type'] == 'file' && inputs[i].files && inputs[i].files[0] && inputs[i].files[0].size) {
                        // Do something or other like compare total file sizes to max request size...
                    }
                }

                return true;
            }

            function ValidateForm2() {
                var inputs = document.getElementsByTagName('input');

                for (var i = 0; i < inputs.length; i++) {
                    // For each file input, check to see if it has a file (each input element can select multiple files,
                    // only look for first file in each input element).
                    if (inputs[i]['type'] == 'file' && inputs[i].files && inputs[i].files[0] && inputs[i].files[0].size) {
                        // Do something or other like compare total file sizes to max request size...
                    }
                    else if (inputs[i]['type'] == 'file') {
                        inputs[i].type = '';     // One of many ways to reset...
                        inputs[i].type = 'file'; // ...the empty file input control
                    }
                }

                return true;
            }
        </script>
    </form>
</body>
</html>

Expected Behavior

I used Fiddler to capture the various POST requests flowing back from the browser to my test web app. Using IE and the first button as an example, here is what I consider to be the expected behavior (since it appears most often across most situations in most browsers).

Only the first and third file input fields are used in this test, to show expected behavior for the second and fourth empty input fields.

There are four test files, Test1.txt through Test4.txt. Each contains something similar to “Test 1 file contents…” for Test1.txt (with the number changed for each file). This makes it easy to see the file contents in the following HTTP POST request and in all the tests at the end.

POST http://localhost:59545/Default/Details/1 HTTP/1.1
Accept: text/html, application/xhtml+xml, image/jxr, */*
Referer: http://localhost:59545/Page1.html
Accept-Language: en-US
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko
Content-Type: multipart/form-data; boundary=---------------------------7e11462140376
Accept-Encoding: gzip, deflate
Content-Length: 764
DNT: 1
Host: localhost:59545
Connection: Keep-Alive
Pragma: no-cache

-----------------------------7e11462140376
Content-Disposition: form-data; name="files"; filename="F:/Test1.txt"
Content-Type: text/plain

Test 1 file contents...
-----------------------------7e11462140376
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


-----------------------------7e11462140376
Content-Disposition: form-data; name="files"; filename="F:/Test3.txt"
Content-Type: text/plain

Test 3 file contents...
-----------------------------7e11462140376
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


-----------------------------7e11462140376--

Note that there are four “slots” in the multipart form content – the first and third have the contents of Test1.txt and Test3.txt, respectively, and the second and fourth are empty.

Problem Behavior

When Javascript in IE (only) iterates through the input elements (triggered by the second button), even if it does nothing to the elements whatsoever, it strips out the empty input elements and only sends the first and third, like this:

POST http://localhost:59545/Default/Details/1 HTTP/1.1
Accept: text/html, application/xhtml+xml, image/jxr, */*
Referer: http://localhost:59545/Page1.html
Accept-Language: en-US
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko
Content-Type: multipart/form-data; boundary=---------------------------7e129ec140376
Accept-Encoding: gzip, deflate
Content-Length: 470
DNT: 1
Host: localhost:59545
Connection: Keep-Alive
Pragma: no-cache

-----------------------------7e129ec140376
Content-Disposition: form-data; name="files"; filename="F:/Test1.txt"
Content-Type: text/plain

Test 1 file contents...
-----------------------------7e129ec140376
Content-Disposition: form-data; name="files"; filename="F:/Test3.txt"
Content-Type: text/plain

Test 3 file contents...
-----------------------------7e129ec140376--

Environment

Windows Server 2016, up to date.

Browser versions:

  • Internet Explorer 11 – 11.1480.14393.0 (update KB4025252)
  • Chrome – 59.0.3071.115
  • Firefox – 54.0.1

Appendix: Fiddler POST Traces

Only the first and third file input fields are used in each test, to show expected behavior for the second and fourth empty input fields.

There are four test files, Test1.txt through Test4.txt. Each contains something similar to “Test 1 file contents…” for Test1.txt (with the number changed for each file). This makes it easy to see the file contents in the HTTP POST request.

Each of the three buttons was tried on IE, Firefox and Chrome, in the following order:

  1. “No Javascript, no worries”
  2. “IE strips empty fields”
  3. “We put them back”

Internet Explorer 11

IE Test 1: “No Javascript, no worries” button

Four multipart form elements sent on POST, as expected.

POST http://localhost:59545/Default/Details/1 HTTP/1.1
Accept: text/html, application/xhtml+xml, image/jxr, */*
Referer: http://localhost:59545/Page1.html
Accept-Language: en-US
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko
Content-Type: multipart/form-data; boundary=---------------------------7e11462140376
Accept-Encoding: gzip, deflate
Content-Length: 764
DNT: 1
Host: localhost:59545
Connection: Keep-Alive
Pragma: no-cache

-----------------------------7e11462140376
Content-Disposition: form-data; name="files"; filename="F:/Test1.txt"
Content-Type: text/plain

Test 1 file contents...
-----------------------------7e11462140376
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


-----------------------------7e11462140376
Content-Disposition: form-data; name="files"; filename="F:/Test3.txt"
Content-Type: text/plain

Test 3 file contents...
-----------------------------7e11462140376
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


-----------------------------7e11462140376--

IE Test 2: “IE strips empty fields” button

Only two multipart form elements sent on POST!

POST http://localhost:59545/Default/Details/1 HTTP/1.1
Accept: text/html, application/xhtml+xml, image/jxr, */*
Referer: http://localhost:59545/Page1.html
Accept-Language: en-US
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko
Content-Type: multipart/form-data; boundary=---------------------------7e129ec140376
Accept-Encoding: gzip, deflate
Content-Length: 470
DNT: 1
Host: localhost:59545
Connection: Keep-Alive
Pragma: no-cache

-----------------------------7e129ec140376
Content-Disposition: form-data; name="files"; filename="F:/Test1.txt"
Content-Type: text/plain

Test 1 file contents...
-----------------------------7e129ec140376
Content-Disposition: form-data; name="files"; filename="F:/Test3.txt"
Content-Type: text/plain

Test 3 file contents...
-----------------------------7e129ec140376--

IE Test 3: “We put them back” button

Four multipart form elements sent on POST, as expected.

POST http://localhost:59545/Default/Details/1 HTTP/1.1
Accept: text/html, application/xhtml+xml, image/jxr, */*
Referer: http://localhost:59545/Page1.html
Accept-Language: en-US
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko
Content-Type: multipart/form-data; boundary=---------------------------7e12d917140376
Accept-Encoding: gzip, deflate
Content-Length: 769
DNT: 1
Host: localhost:59545
Connection: Keep-Alive
Pragma: no-cache

-----------------------------7e12d917140376
Content-Disposition: form-data; name="files"; filename="F:/Test1.txt"
Content-Type: text/plain

Test 1 file contents...
-----------------------------7e12d917140376
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


-----------------------------7e12d917140376
Content-Disposition: form-data; name="files"; filename="F:/Test3.txt"
Content-Type: text/plain

Test 3 file contents...
-----------------------------7e12d917140376
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


-----------------------------7e12d917140376--

Firefox

Firefox Test 1: “No Javascript, no worries” button

Four multipart form elements sent on POST, as expected.

POST http://localhost:59545/Default/Details/1 HTTP/1.1
Host: localhost:59545
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------225491224652
Content-Length: 665
Referer: http://localhost:59545/Page1.html
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1

-----------------------------225491224652
Content-Disposition: form-data; name="files"; filename="Test1.txt"
Content-Type: text/plain

Test 1 file contents...
-----------------------------225491224652
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


-----------------------------225491224652
Content-Disposition: form-data; name="files"; filename="Test3.txt"
Content-Type: text/plain

Test 3 file contents...
-----------------------------225491224652
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


-----------------------------225491224652--

Firefox Test 2: “IE strips empty fields” button

Four multipart form elements sent on POST, as expected.

POST http://localhost:59545/Default/Details/1 HTTP/1.1
Host: localhost:59545
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------20141440714759
Content-Length: 675
Referer: http://localhost:59545/Page1.html
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1

-----------------------------20141440714759
Content-Disposition: form-data; name="files"; filename="Test1.txt"
Content-Type: text/plain

Test 1 file contents...
-----------------------------20141440714759
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


-----------------------------20141440714759
Content-Disposition: form-data; name="files"; filename="Test3.txt"
Content-Type: text/plain

Test 3 file contents...
-----------------------------20141440714759
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


-----------------------------20141440714759--

Firefox Test 3: “We put them back” button

Four multipart form elements sent on POST, as expected.

POST http://localhost:59545/Default/Details/1 HTTP/1.1
Host: localhost:59545
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------16987275582373
Content-Length: 675
Referer: http://localhost:59545/Page1.html
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1

-----------------------------16987275582373
Content-Disposition: form-data; name="files"; filename="Test1.txt"
Content-Type: text/plain

Test 1 file contents...
-----------------------------16987275582373
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


-----------------------------16987275582373
Content-Disposition: form-data; name="files"; filename="Test3.txt"
Content-Type: text/plain

Test 3 file contents...
-----------------------------16987275582373
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


-----------------------------16987275582373--

Chrome

Chrome Test 1: “No Javascript, no worries” button

Four multipart form elements sent on POST, as expected.

POST http://localhost:59545/Default/Details/1 HTTP/1.1
Host: localhost:59545
Connection: keep-alive
Content-Length: 660
Cache-Control: max-age=0
Origin: http://localhost:59545
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarysARPkT6Y1fHbw9sF
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://localhost:59545/Page1.html
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8

------WebKitFormBoundarysARPkT6Y1fHbw9sF
Content-Disposition: form-data; name="files"; filename="Test1.txt"
Content-Type: text/plain

Test 1 file contents...
------WebKitFormBoundarysARPkT6Y1fHbw9sF
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


------WebKitFormBoundarysARPkT6Y1fHbw9sF
Content-Disposition: form-data; name="files"; filename="Test3.txt"
Content-Type: text/plain

Test 3 file contents...
------WebKitFormBoundarysARPkT6Y1fHbw9sF
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


------WebKitFormBoundarysARPkT6Y1fHbw9sF--

Chrome Test 2: “IE strips empty fields” button

Four multipart form elements sent on POST, as expected.

POST http://localhost:59545/Default/Details/1 HTTP/1.1
Host: localhost:59545
Connection: keep-alive
Content-Length: 660
Cache-Control: max-age=0
Origin: http://localhost:59545
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryVYNZIw4uo5TwOmXG
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://localhost:59545/Page1.html
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8

------WebKitFormBoundaryVYNZIw4uo5TwOmXG
Content-Disposition: form-data; name="files"; filename="Test1.txt"
Content-Type: text/plain

Test 1 file contents...
------WebKitFormBoundaryVYNZIw4uo5TwOmXG
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


------WebKitFormBoundaryVYNZIw4uo5TwOmXG
Content-Disposition: form-data; name="files"; filename="Test3.txt"
Content-Type: text/plain

Test 3 file contents...
------WebKitFormBoundaryVYNZIw4uo5TwOmXG
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


------WebKitFormBoundaryVYNZIw4uo5TwOmXG--

Chrome Test 3: “We put them back” button

Four multipart form elements sent on POST, as expected.

POST http://localhost:59545/Default/Details/1 HTTP/1.1
Host: localhost:59545
Connection: keep-alive
Content-Length: 660
Cache-Control: max-age=0
Origin: http://localhost:59545
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryI6IuNQdTSMb21vHW
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://localhost:59545/Page1.html
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8

------WebKitFormBoundaryI6IuNQdTSMb21vHW
Content-Disposition: form-data; name="files"; filename="Test1.txt"
Content-Type: text/plain

Test 1 file contents...
------WebKitFormBoundaryI6IuNQdTSMb21vHW
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


------WebKitFormBoundaryI6IuNQdTSMb21vHW
Content-Disposition: form-data; name="files"; filename="Test3.txt"
Content-Type: text/plain

Test 3 file contents...
------WebKitFormBoundaryI6IuNQdTSMb21vHW
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream


------WebKitFormBoundaryI6IuNQdTSMb21vHW--