I'm trying to avoid the dreaded " picker was blocked due to lack of user activation" error when clicking on an <input type="file">
element. I know you think you're seen this question before many times, but wait. This one is different.
Most/all other questions are about dispatching a click event on a file input element programmatically (.click()
), but having it fail to open a picker for lack of user activation (i.e., a physical click).
In JavaScript can I make a "click" event fire programmatically for a file input element?
How can I programmatically open the file picker with JavaScript?
Firefox can't override "<input> picker was blocked due to lack of user activation" in about:config
This question involves physically clicking the file input element, but checking interactively if the user really wants to select a file now.
I've implemented (a simplified version of) that ideal flow with the first "button" below. However, there's a problem. If the user waits more than 5 seconds to decide that it's okay to load the file, the picker refuses to open with the "lack of user activation" error logged in the console.
Less ideally, I've implemented an alternate flow with the second "button" below.
( function () {
'use strict';
function onChange1( e ) {
alert( 'contents overwritten' );
}
function onClick1( e ) {
if (
!confirm(
'There are unsaved changes.\n' +
'Is it OK to discard them?' )
) {
e.preventDefault();
}
}
function onChange2() {
if (
confirm(
'There are unsaved changes.\n' +
'Is it OK to discard them?' )
) {
alert( 'contents overwritten' );
}
}
function onContentLoaded() {
var
input1 = document.getElementById( 'input1' ),
input2 = document.getElementById( 'input2' );
input1.addEventListener( 'click', onClick1 );
input1.addEventListener( 'change', onChange1 );
input2.addEventListener( 'change', onChange2 );
}
document.addEventListener( 'DOMContentLoaded', onContentLoaded, { once: true } );
} () );
div {
background-color: #eee;
display: inline-block;
}
input {
display: none;
}
label {
line-height: 2;
padding: 0.5em;
}
<!DOCTYPE html><html lang="en"><head>
<title>Stack Overflow QA</title>
<link rel="stylesheet" href="page.css">
<script src="page.js"></script>
</head><body>
<div><label><input id="input1" type="file">Open File 1 ...</label></div>
<div><label><input id="input2" type="file">Open File 2 ...</label></div>
</body></html>
I really prefer the first flow, so I'd like to find a way around the 5-second timeout. I'm specifically interested in a Firefox solution, even if it might apply to other browsers, as well.
Thanks to @GuyIncognito's comment, here's my (minimal) solution.
The OK of the confirmation dialog is the user activation which is passed to the file picker. This solution follows the desired flow in the question.
( function () {
'use strict';
function onChange() {
// just a status update, nothing real
alert( 'contents overwritten' );
}
function onClickDialog( e ) {
if ( e.target.id === 'OK' ) {
// pass the click along
document.getElementById( 'input' ).click();
}
// re-hide the dialog
document.getElementById( 'modal' ).classList = 'hidden';
}
function onClickFile() {
// unhide the dialog
document.getElementById( 'modal' ).classList = '';
}
function onContentLoaded() {
document.getElementById( 'button' ).addEventListener( 'click', onClickFile );
document.getElementById( 'OK' ).addEventListener( 'click', onClickDialog );
document.getElementById( 'Cancel' ).addEventListener( 'click', onClickDialog );
document.getElementById( 'input' ).addEventListener( 'change', onChange )
}
document.addEventListener( 'DOMContentLoaded', onContentLoaded, { once: true } );
} () );
button {
font-size: inherit;
}
input,
.hidden {
display: none;
}
#modal {
background-color: #0001;
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
}
#dialog {
background-color: #fff;
display: inline-block;
left: 50%;
padding: 1em;
position: relative;
top:50%;
transform: translate( -50%, -50% );
}
<!DOCTYPE html><html lang="en"><head>
<title>Stack Overflow QA</title>
<link rel="stylesheet" href="page.css">
<script src="page.js"></script>
</head><body>
<div id="modal" class="hidden"><div id="dialog">
<p>There are unsaved changes.</p>
<p>Is it OK to discard them?</p>
<button id="OK">OK</button> <button id="Cancel">Cancel</button>
</div></div>
<button id="button">Open File ...</button>
<input id="input" type="file">
</body></html>