17

<input type="file"/> The file and it path is cleared after clicking cancel in 'choose file' modal window in chrome, in FF and IE file stays untouched after pressing cancel. Is there any way to change this behavior in chrome?

Powerslave
  • 321
  • 1
  • 2
  • 9

4 Answers4

2

https://jsfiddle.net/dqL97q0b/1/

Here is a work around, so that Chrome cannot remove the users existing file when cancel is pressed.

Code Notes: This makes a clone of the Dom Element when User opens the file chooser if there is already a file selected. Then if the user does click cancel in chrome it fires the change Event Listener and the value Will be "", so in that specific case I remove the now empty file chooser, and restore the clone.

Note: each file chooser Dom Element needs a unique id, so that the clone can be stored and retrieved properly.

Note: Most of the code is just logging, to show how things work, specifically I wanted to highlight that if you use the inline event listeners in the Dom Element such as onclick="fileClicked(event)" then you do not need to re-attach event listeners to the clone.

<!doctype html><html><head></head><body>

<h2>Fix for Chrome Removing File when 'cancel' clicked</h2>

Upload Image: <input id="imageUpload" type="file" onclick="fileClicked(event)" onchange="fileChanged(event)">
<br/><br/>
<label for="videoUpload">Upload Video:</label> <input id="videoUpload" type="file" onclick="fileClicked(event)" onchange="fileChanged(event)">
<br/><br/>
<div id="log"></div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
    //This is All Just For Logging:
    var debug = true;//true: add debug logs when cloning
    var evenMoreListeners = true;//demonstrat re-attaching javascript Event Listeners (Inline Event Listeners don't need to be re-attached)
    if (evenMoreListeners) {
        var allFleChoosers = $("input[type='file']");
        addEventListenersTo(allFleChoosers);
        function addEventListenersTo(fileChooser) {
            fileChooser.change(function (event) { console.log("file( #" + event.target.id + " ) : " + event.target.value.split("\\").pop()) });
            fileChooser.click(function (event) { console.log("open( #" + event.target.id + " )") });
        }
    }

    var clone = {};

    // FileClicked()
    function fileClicked(event) {
        var fileElement = event.target;
        if (fileElement.value != "") {
            if (debug) { console.log("Clone( #" + fileElement.id + " ) : " + fileElement.value.split("\\").pop()) }
            clone[fileElement.id] = $(fileElement).clone(); //'Saving Clone'
        }
        //What ever else you want to do when File Chooser Clicked
    }

    // FileChanged()
    function fileChanged(event) {
        var fileElement = event.target;
        if (fileElement.value == "") {
            if (debug) { console.log("Restore( #" + fileElement.id + " ) : " + clone[fileElement.id].val().split("\\").pop()) }
            clone[fileElement.id].insertBefore(fileElement); //'Restoring Clone'
            $(fileElement).remove(); //'Removing Original'
            if (evenMoreListeners) { addEventListenersTo(clone[fileElement.id]) }//If Needed Re-attach additional Event Listeners
        }
        //What ever else you want to do when File Chooser Changed
    }
    </script>
</body></html>

https://jsfiddle.net/dqL97q0b/1/

Nick Timmer
  • 425
  • 2
  • 12
1

What this doesn't cover

This solution doesn't cover Chrome's initial reason for not introducing this natively, a remove button. Though, with this considered, a remove button would be a super simple implementation.

What does this do?

This code saves the chosen file to an array as an object whenever a file is chosen on the input, retrieving that whenever the file input does not specify a file choice. (cancelled)

Solution

// Globally declare the array
// If your javascript isn't refreshed each time you visit the inputs, you'll want to clear this on form submit
array = []

// Add New file
// Replace current file
$("input").change(function(event) {
   let file_list = event.target.files
   let key = event.target.id

   // When we change the files in our input, we persist the file by input
   // Persistence before assignment will always result in the most recent file choice
   persist_file(array, file_list, key)

   // Assign the targets files to whatever is persisted for it
   event.target.files = element_for(array, key).file_list 
});

// @doc Pushes or Replaces {key: <key>, file_list: <FileList>} objects into an array
function persist_file(array, file_list, key) {
 if(file_list.length > 0) {
   if(member(array, key)) {
     element_for(array, key).file_list = file_list;
    }else {
     array.push({key: key, file_list: file_list})
    }
  }
}

// @doc Determines if the <key> exists in an object element of the <array>
function member(array, key) {
  return array.some((element, index) => {
    return element.key == key
  })
}

// @doc Get element in array by key
function element_for(array, key) {
  return array.find((function(obj, index) {return obj.key === key}))
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type='file' id="input_0"/><br>
<input type='file' id="input_1"/><br>
<input type='file' id="input_2"/>
Thomas
  • 2,622
  • 1
  • 9
  • 16
0

Yes, that's been an issue with chrome if you want a jquery based solution you could use the following.

// Defining a global variable to persist data
let $old_value;

// if input field id is file and its inside a div which has id file-wrapper
$('#file_wrapper').on('change', '#file', function() {
    const img = this.files[0];

    // If image selected
    if (img) {
       // Change thumbnail or what your want

       // Cloning the input filed value for future
       old_value = $(this).clone();
    } else {
       // If no file selected or canceled and previously selected an image
       if (old_value) {
           $(this).remove();
           let new_img = $('#file_wrapper').append(old_img);
           old_img = new_img.clone();
       }
    }
}
Aslam-Ep
  • 61
  • 5
-1

Simple yet effective solution: keep a reference to the file in a variable. Every time the change event of the input is fired you check:

if(input.files[0]) // truthy, falsey
{
  var file = input.files[0];
}

So file will contain the latest selected file and when user opens the dialog but cancelled out the file reference won't be changed and still contains the previously selected file.

Youp Bernoulli
  • 5,303
  • 5
  • 39
  • 59
  • No, I felt that while it provided a solution to hand-rolling the behavior the OP was looking for, it didn't actually answer their question of "Can I change the way this works by default in Chrome?"; @Medhi's answer most directly answers the question, but lacks a suggested solution to the problem. I think an answer worth upvoting should have both components. – Daniel Brady Jul 19 '19 at 14:53