How to Detect When Cancel Is Clicked on File Input

Cancel event on input type=file

A bit of research indicates that there is no way to detect when Cancel is selected in the File Selection dialog window. You can use onchange or onblur to check if files have been selected or if something has been added to the input value.

This could look like: https://jsfiddle.net/Twisty/j18td9cs/

HTML

<form>
Select File:
<input type="file" name="test1" id="testFile" />
<button type="reset" id="pseudoCancel">
Cancel
</button>
</form>

JavaScript

var inputElement = document.getElementById("testFile");
var cancelButton = document.getElementById("pseudoCancel");
var numFiles = 0;

inputElement.onclick = function(event) {
var target = event.target || event.srcElement;
console.log(target, "clicked.");
console.log(event);
if (target.value.length == 0) {
console.log("Suspect Cancel was hit, no files selected.");
cancelButton.onclick();
} else {
console.log("File selected: ", target.value);
numFiles = target.files.length;
}
}

inputElement.onchange = function(event) {
var target = event.target || event.srcElement;
console.log(target, "changed.");
console.log(event);
if (target.value.length == 0) {
console.log("Suspect Cancel was hit, no files selected.");
if (numFiles == target.files.length) {
cancelButton.onclick();
}
} else {
console.log("File selected: ", target.value);
numFiles = target.files.length;
}
}

inputElement.onblur = function(event) {
var target = event.target || event.srcElement;
console.log(target, "changed.");
console.log(event);
if (target.value.length == 0) {
console.log("Suspect Cancel was hit, no files selected.");
if (numFiles == target.files.length) {
cancelButton.onclick();
}
} else {
console.log("File selected: ", target.value);
numFiles = target.files.length;
}
}


cancelButton.onclick = function(event) {
console.log("Pseudo Cancel button clicked.");
}

I suggest making your own cancel or reset button that resets the form or clears the value from the input.

How can I detect that the Cancel Button has been clicked on a input type=file / Select File Dialog?

I've invested countless hours looking for a solution. And now I want to share my solution with you.

I use three event handlers:

  1. onchange event on the file input: To detect when a file has been selected.

  2. onfocus event on window: To detect when the Select File Dialog has been closed.

  3. onmousemove event on document.body: To detect when the interaction is not blocked anymore. Only when this event is called, you can be sure that the onchange event of the input element has been called.

The first two points are obvious and you find them in most proposed solutions. But the crucial point is number 3. In other solutions I sometimes faced the problem that I selected a file, but this selected file has not been propagated to the onchange event handler before window's got focus.

To make a long story short, here's my implementation:

TypeScript solution:

public static selectFile(accept: string = null): Promise<File> {
return new Promise<File>(async resolve => {
const fileInputElement = document.createElement('input') as HTMLInputElement;
fileInputElement.type = 'file';
fileInputElement.style.opacity = '0';
if (accept) fileInputElement.accept = accept;
fileInputElement.addEventListener('change', () => {
const file = fileInputElement.files[0];
console.log('File "' + file.name + '" selected.');
document.body.removeChild(fileInputElement);
resolve(file);
});
document.body.appendChild(fileInputElement);
setTimeout(_ => {
fileInputElement.click();
const onFocus = () => {
window.removeEventListener('focus', onFocus);
document.body.addEventListener('mousemove', onMouseMove);
};
const onMouseMove = () => {
document.body.removeEventListener('mousemove', onMouseMove);
if (!fileInputElement.files.length) {
document.body.removeChild(fileInputElement);
console.log('No file selected.');
resolve(null);
}
}
window.addEventListener('focus', onFocus);
}, 0);
});
}

JavaScript solution:

function selectFile(accept = null) {
return new Promise(async resolve => {
const fileInputElement = document.createElement('input');
fileInputElement.type = 'file';
fileInputElement.style.opacity = '0';
if (accept) fileInputElement.accept = accept;
fileInputElement.addEventListener('change', () => {
const file = fileInputElement.files[0];
console.log('File "' + file.name + '" selected.');
document.body.removeChild(fileInputElement);
resolve(file);
});
document.body.appendChild(fileInputElement);
setTimeout(_ => {
fileInputElement.click();
const onFocus = () => {
window.removeEventListener('focus', onFocus);
document.body.addEventListener('mousemove', onMouseMove);
};
const onMouseMove = () => {
document.body.removeEventListener('mousemove', onMouseMove);
if (!fileInputElement.files.length) {
document.body.removeChild(fileInputElement);
console.log('No file selected.');
resolve(null);
}
}
window.addEventListener('focus', onFocus);
}, 0);
});
}

ReactJS - How to detect when cancel is clicked on file input?

Well, this is probably what I would do but I don't really like it, also I think that the whole idea of performing something because the user decided not to upload a file isn't a good user experience so use it wisely.

fileInputClicked(event){
let file = event.target.files[0];
console.log("File is Selected", file);
window.removeEventListener('focus', this.handleFocusBack);
}

handleFocusBack(){
console.log('focus-back');
window.removeEventListener('focus', this.handleFocusBack);
}

clickedFileInput(){
window.addEventListener('focus', this.handleFocusBack);
}

render() {
return (
<div>
<div>
<p>Hello world</p>
<input onClick={this.clickedFileInput} type="file" onChange={this.fileInputClicked}/>
</div>
</div>
);
}

This way, when the user decides to close the file input, he focuses back on the window and you get the handleFocusBack functionality you want and when he adds a file he goes to the fileInputClicked.

Afaik, there is no 'out of the box' mechanism to achieve what you want.



Related Topics



Leave a reply



Submit