Comment puis-je détecter le moment où l'utilisateur annule une entrée de fichier à l'aide d'une entrée de fichier html?
onChange me permet de détecter quand ils choisissent un fichier, mais j'aimerais aussi savoir quand ils annulent (ferme le dialogue de choix du fichier sans rien sélectionner).
Bien que ce ne soit pas une solution directe, mais également une mauvaise solution dans la mesure où cela fonctionne uniquement (dans la mesure de mes essais) avec onfocus (nécessitant un blocage d’événements assez limitant), vous pouvez y parvenir avec les éléments suivants:
document.body.onfocus = function(){ /*rock it*/ }
Ce qui est bien à propos de cela, c’est que vous pouvez l’attacher/le détacher à temps avec l’événement de fichier, et cela semble également fonctionner correctement avec les entrées cachées (un avantage certain si vous utilisez une solution de contournement visuelle pour le type d’entrée par défaut = ' fichier'). Après cela, il vous suffit de déterminer si la valeur d'entrée a changé.
Un exemple:
var godzilla = document.getElementById('godzilla')
godzilla.onclick = charge
function charge()
{
document.body.onfocus = roar
console.log('chargin')
}
function roar()
{
if(godzilla.value.length) alert('ROAR! FILES!')
else alert('*empty wheeze*')
document.body.onfocus = null
console.log('depleted')
}
Voir en action: http://jsfiddle.net/Shiboe/yuK3r/6/
Malheureusement, cela ne semble fonctionner que sur les navigateurs Webkit. Peut-être que quelqu'un d'autre peut comprendre la solution firefox/IE
Tu ne peux pas.
Le résultat de la boîte de dialogue de fichier n'est pas exposé au navigateur.
/* Tested on Google Chrome */
$("input[type=file]").bind("change", function() {
var selected_file_name = $(this).val();
if ( selected_file_name.length > 0 ) {
/* Some file selected */
}
else {
/* No file selected or cancel/close
dialog button clicked */
/* If user has select a file before,
when they submit, it will treated as
no file selected */
}
});
Lorsque vous sélectionnez un fichier et cliquez sur Ouvrir/Annuler, l'élément input
devrait perdre le focus, également appelé blur
. En supposant que la variable value
de input
soit vide, toute valeur non vide de votre gestionnaire blur
indiquerait OK et une valeur vide signifierait Annuler.
UPDATE: La blur
n'est pas déclenchée lorsque la input
est masquée. Donc, vous ne pouvez pas utiliser cette astuce avec des téléchargements basés sur IFRAME, sauf si vous souhaitez afficher temporairement la variable input
.
Écoutez simplement l'événement de clic.
Pour reprendre l'exemple de Shiboe, voici un exemple de jQuery:
var godzilla = $('#godzilla');
var godzillaBtn = $('#godzilla-btn');
godzillaBtn.on('click', function(){
godzilla.trigger('click');
});
godzilla.on('change click', function(){
if (godzilla.val() != '') {
$('#state').html('You have chosen a Mech!');
} else {
$('#state').html('Choose your Mech!');
}
});
Vous pouvez le voir en action ici: http://jsfiddle.net/T3Vwz
Vous pouvez intercepter l'annulation si vous choisissez le même fichier que précédemment et que vous cliquez sur Annuler: dans ce cas.
Vous pouvez le faire comme ça:
<input type="file" id="myinputfile"/>
<script>
document.getElementById('myinputfile').addEventListener('change', myMethod, false);
function myMethod(evt) {
var files = evt.target.files;
f= files[0];
if (f==undefined) {
// the user has clicked on cancel
}
else if (f.name.match(".*\.jpg")|| f.name.match(".*\.png")) {
//.... the user has choosen an image file
var reader = new FileReader();
reader.onload = function(evt) {
try {
myimage.src=evt.target.result;
...
} catch (err) {
...
}
};
}
reader.readAsDataURL(f);
</script>
Le moyen le plus simple est de vérifier s’il existe des fichiers dans la mémoire temporaire. Si vous souhaitez obtenir l'événement change chaque fois que l'utilisateur clique sur l'entrée du fichier, vous pouvez le déclencher.
var yourFileInput = $("#yourFileInput");
yourFileInput.on('mouseup', function() {
$(this).trigger("change");
}).on('change', function() {
if (this.files.length) {
//User chose a picture
} else {
//User clicked cancel
}
});
La plupart de ces solutions ne fonctionnent pas pour moi.
Le problème est que vous ne savez jamais quel événement sera déclenché poing, Est-ce click
ou est-ce change
? Vous ne pouvez assumer aucun ordre, car cela dépend probablement de la mise en œuvre du navigateur.
Au moins dans Opera et Chrome (fin 2015), click
est déclenché juste avant de 'remplir' une entrée avec des fichiers, de sorte que vous ne saurez jamais la longueur de files.length != 0
tant que vous n'aurez pas retardé le click
après change
.
Voici le code:
var inputfile = $("#yourid");
inputfile.on("change click", function(ev){
if (ev.originalEvent != null){
console.log("OK clicked");
}
document.body.onfocus = function(){
document.body.onfocus = null;
setTimeout(function(){
if (inputfile.val().length === 0) console.log("Cancel clicked");
}, 1000);
};
});
La solution de Shiboe serait une bonne solution si elle fonctionnait sur le Webkit mobile, mais ce n’est pas le cas. Ce que je peux imaginer, c’est d’ajouter un écouteur d’événement mousemove à un objet dom au moment de l’ouverture de la fenêtre de saisie du fichier, comme suit:
$('.upload-progress').mousemove(function() {
checkForFiles(this);
});
checkForFiles = function(me) {
var filefield = $('#myfileinput');
var files = filefield.get(0).files;
if (files == undefined || files[0] == undefined) $(me).remove(); // user cancelled the upload
};
L'événement mousemove est bloqué à partir de la page lorsque la boîte de dialogue de fichier est ouverte et lorsqu'elle est fermée, elle vérifie s'il existe des fichiers dans l'entrée de fichier. Dans mon cas, je veux un indicateur d’activité bloquant les choses jusqu’à ce que le fichier soit chargé. Je souhaite donc uniquement supprimer mon indicateur lors de l’annulation.
Cependant, cela ne résout pas le problème de la mobilité, car il n’ya pas de souris à déplacer. Ma solution est moins que parfaite, mais je pense que cela suffit.
$('.upload-progress').bind('touchstart', function() {
checkForFiles(this);
});
Maintenant, nous sommes à l’écoute d’un appui sur l’écran pour vérifier les mêmes fichiers. Je suis assez confiant que le doigt de l'utilisateur sera affiché à l'écran assez rapidement après l'annulation et la suppression de cet indicateur d'activité.
On pourrait aussi simplement ajouter l'indicateur d'activité à l'événement de modification d'entrée de fichier, mais sur mobile, il s'écoule souvent quelques secondes entre la sélection de l'image et l'activation de l'événement de modification. début du processus.
Je sais que c’est une très vieille question, mais au cas où cela aiderait quelqu'un, j’ai découvert lorsqu’on utilisait l’événement onmousemove pour détecter l’annulation qu’il était nécessaire de tester deux événements ou plus de ce type dans un laps de temps bref . En effet, le navigateur (Chrome 65) générait des événements uniques onmousemove chaque fois que le curseur est déplacé hors de la fenêtre de dialogue de sélection de fichier et chaque fois qu'il est déplacé hors de la fenêtre principale puis à nouveau dans . les mouvements de la souris associés à un délai d’exécution de courte durée pour réinitialiser le compteur à zéro ont fonctionné.
Je vais donc lancer mon chapeau à cette question, car j'ai mis au point une nouvelle solution. J'ai une application Web progressive qui permet aux utilisateurs de capturer des photos et des vidéos et de les télécharger. Nous utilisons WebRTC dans la mesure du possible, mais nous revenons aux sélecteurs de fichiers HTML5 pour les périphériques moins pris en charge * Cough Safari Cough *. Si vous travaillez spécifiquement sur une application Web mobile Android/iOS qui utilise l'appareil photo natif pour capturer directement des photos/vidéos, c'est la meilleure solution que j'ai trouvée.
Le nœud de ce problème est que, lorsque la page est chargée, la file
est null
, mais lorsque l'utilisateur ouvre la boîte de dialogue et appuie sur "Annuler", la file
est toujours null
, elle n'a donc pas "changé", donc pas de "modification". l'événement est déclenché. Pour les ordinateurs de bureau, ce n’est pas si grave, car la plupart des interfaces utilisateur ne dépendent pas du fait de savoir quand une annulation est invoquée, mais les interfaces utilisateur mobiles qui permettent à la caméra de capturer une photo/vidéo sont très dépendent de savoir quand un annuler est appuyé.
À l'origine, j'avais utilisé l'événement document.body.onfocus
pour détecter le retour de l'utilisateur à partir du sélecteur de fichiers, ce qui a fonctionné pour la plupart des appareils, mais iOS 11.3 l'a cassé car cet événement n'était pas déclenché.
Ma solution à ce problème est de * frémir * pour mesurer le temps du processeur afin de déterminer si la page est actuellement au premier plan ou à l’arrière-plan. Sur les appareils mobiles, le temps de traitement est donné à l'application actuellement au premier plan. Lorsqu'une caméra est visible, elle vole du temps processeur et dépériorise le navigateur. Tout ce que nous avons à faire est de mesurer le temps de traitement accordé à notre page. Lors du lancement de la caméra, notre temps disponible diminuera considérablement. Lorsque la caméra est rejetée (annulée ou non), notre temps disponible est rétabli.
Nous pouvons mesurer le rythme du processeur en utilisant setTimeout()
pour appeler un rappel en X millisecondes, puis mesurer le temps nécessaire pour l'appeler. Le navigateur ne l'invoquera jamais exactement après X millisecondes, mais s'il est proche, vous devez être au premier plan. Si le navigateur est très éloigné (plus de 10 fois plus lent que prévu), nous devons être en arrière-plan. Une implémentation de base de ceci ressemble à ceci:
function waitForCameraDismiss() {
const REQUESTED_DELAY_MS = 25;
const ALLOWED_MARGIN_OF_ERROR_MS = 25;
const MAX_REASONABLE_DELAY_MS =
REQUESTED_DELAY_MS + ALLOWED_MARGIN_OF_ERROR_MS;
const MAX_TRIALS_TO_RECORD = 10;
const triggerDelays = [];
let lastTriggerTime = Date.now();
return new Promise((resolve) => {
const evtTimer = () => {
// Add the time since the last run
const now = Date.now();
triggerDelays.Push(now - lastTriggerTime);
lastTriggerTime = now;
// Wait until we have enough trials before interpreting them.
if (triggerDelays.length < MAX_TRIALS_TO_RECORD) {
window.setTimeout(evtTimer, REQUESTED_DELAY_MS);
return;
}
// Only maintain the last few event delays as trials so as not
// to penalize a long time in the camera and to avoid exploding
// memory.
if (triggerDelays.length > MAX_TRIALS_TO_RECORD) {
triggerDelays.shift();
}
// Compute the average of all trials. If it is outside the
// acceptable margin of error, then the user must have the
// camera open. If it is within the margin of error, then the
// user must have dismissed the camera and returned to the page.
const averageDelay =
triggerDelays.reduce((l, r) => l + r) / triggerDelays.length
if (averageDelay < MAX_REASONABLE_DELAY_MS) {
// Beyond any reasonable doubt, the user has returned from the
// camera
resolve();
} else {
// Probably not returned from camera, run another trial.
window.setTimeout(evtTimer, REQUESTED_DELAY_MS);
}
};
window.setTimeout(evtTimer, REQUESTED_DELAY_MS);
});
}
J'ai testé cela sur les versions récentes d'iOS et d'Android, en élevant la caméra native en définissant les attributs sur l'élément <input />
.
<input type="file" accept="image/*" capture="camera" />
<input type="file" accept="video/*" capture="camcorder" />
Cela fonctionne en fait beaucoup mieux que prévu. Il exécute 10 essais en demandant qu'un minuteur soit appelé dans 25 millisecondes. Il mesure ensuite le temps qu'il a réellement fallu pour invoquer et si la moyenne de 10 essais est inférieure à 50 millisecondes, nous supposons que nous devons être au premier plan et que la caméra est partie. S'il est supérieur à 50 millisecondes, nous devons rester en arrière-plan et continuer d'attendre.
J'ai utilisé setTimeout()
plutôt que setInterval()
car ce dernier peut mettre en file d'attente plusieurs appels qui s'exécutent immédiatement les uns après les autres. Cela pourrait considérablement augmenter le bruit dans nos données, alors je me suis contenté de setTimeout()
même si c'est un peu plus compliqué de le faire.
Ces chiffres particuliers ont bien fonctionné pour moi, même si j'ai déjà vu au moins une fois où la caméra a été détectée prématurément. Je pense que cela est dû au fait que l'appareil photo peut être lent à s'ouvrir et que le périphérique peut exécuter 10 essais avant de devenir réellement en arrière-plan. L'ajout d'essais supplémentaires ou l'attente de 25 à 50 millisecondes avant de démarrer cette fonction peut constituer une solution de contournement.
Malheureusement, cela ne fonctionne pas vraiment pour les navigateurs de bureau. En théorie, le même truc est possible car ils donnent la priorité à la page actuelle par rapport aux pages avec arrière-plan. Cependant, de nombreux ordinateurs de bureau disposent de suffisamment de ressources pour que la page continue de fonctionner à plein régime, même en arrière-plan, de sorte que cette stratégie ne fonctionne pas vraiment dans la pratique.
Une solution alternative que peu de gens ont mentionnée que j'ai explorée était de se moquer d'une FileList
. Nous commençons par null
dans le <input />
, puis si l'utilisateur ouvre la caméra et annule, il revient à null
, ce qui ne constitue pas une modification et aucun événement ne se déclenchera. Une solution consisterait à affecter un fichier factice au <input />
au début de la page. Par conséquent, la définition de null
constituerait une modification qui déclencherait l'événement approprié.
Malheureusement, il n’existe aucun moyen officiel de créer une FileList
, et l’élément <input />
requiert en particulier une FileList
et n’acceptera aucune autre valeur à part null
. Naturellement, les objets FileList
ne peuvent pas être construits directement, à cause d'un ancien problème de sécurité qui n'est même plus apparemment apparent. La seule façon d'obtenir un élément en dehors d'un élément <input />
consiste à utiliser un hack qui copie-coller les données pour simuler un événement presse-papiers pouvant contenir un objet FileList
événement sur votre site Web). Ceci est possible dans Firefox, mais pas pour iOS Safari, ce n'était donc pas viable pour mon cas d'utilisation particulier.
Cependant , les vendeurs de navigateurs peuvent-ils reconnaître la réalité? Cela pourrait être résolu avec un nouvel événement "terminé" qui serait déclenché même si aucun changement ne se produirait, ou vous pourriez simplement déclencher un "changement" de toute façon. Oui, ce serait contre les spécifications, mais il est trivial pour moi de dédoubler un événement de changement du côté de JavaScript, mais fondamentalement impossible d'inventer mon propre événement "fait". Même ma solution n'est que de simples heuristiques, si nous n'offrons aucune garantie quant à l'état du navigateur.
Dans l'état actuel des choses, cette API est fondamentalement inutilisable pour les appareils mobiles, et je pense qu'un changement de navigateur relativement simple pourrait rendre cela infiniment plus facile pour les développeurs Web *.
As it stands, this API is fundamentally unusable for mobile devices, and I think a relatively simple browser change could make this infinitely easier for web developers *steps off soap box*.
Vous ne pouvez le détecter que dans des circonstances limitées. Plus précisément, dans Chrome, si un fichier a été sélectionné précédemment, puis que la boîte de dialogue de fichier est cliquée et que vous cliquez sur Annuler, Chrome efface le fichier et déclenche l'événement onChange.
https://code.google.com/p/chromium/issues/detail?id=2508
Dans ce scénario, vous pouvez le détecter en gérant l'événement onChange et en vérifiant la propriété fichiers.
Je vois que ma réponse serait assez dépassée, mais néanmoins ... .. J'ai le même problème. Voici donc ma solution ... Le code le plus utile a été celui de KGA. Mais cela ne fonctionne pas totalement et est un peu compliqué. Mais je l'ai simplifié.
En outre, le principal fauteur de troubles était le fait que cet événement de «changement» ne se produit pas instantanément après la mise au point, nous devons donc attendre un peu.
"#appendfile" - L'utilisateur clique dessus pour ajouter un nouveau fichier . Les Hrefs obtiennent des événements de focus.
$("#appendfile").one("focusin", function () {
// no matter - user uploaded file or canceled,
// appendfile gets focus
// change doesn't come instantly after focus, so we have to wait for some time
// wrapper represents an element where a new file input is placed into
setTimeout(function(){
if (wrapper.find("input.fileinput").val() != "") {
// user has uploaded some file
// add your logic for new file here
}
else {
// user canceled file upload
// you have to remove a fileinput element from DOM
}
}, 900);
});
En combinant les solutions Shiboe et alx, j'ai le code le plus fiable:
var selector = $('<input/>')
.attr({ /* just for example, use your own attributes */
"id": "FilesSelector",
"name": "File",
"type": "file",
"contentEditable": "false" /* if you "click" on input via label, this prevents IE7-8 from just setting caret into file input's text filed*/
})
.on("click.filesSelector", function () {
/* do some magic here, e.g. invoke callback for selection begin */
var cancelled = false; /* need this because .one calls handler once for each event type */
setTimeout(function () {
$(document).one("mousemove.filesSelector focusin.filesSelector", function () {
/* namespace is optional */
if (selector.val().length === 0 && !cancelled) {
cancelled = true; /* prevent double cancel */
/* that's the point of cancel, */
}
});
}, 1); /* 1 is enough as we just need to delay until first available tick */
})
.on("change.filesSelector", function () {
/* do some magic here, e.g. invoke callback for successful selection */
})
.appendTo(yourHolder).end(); /* just for example */
Généralement, l'événement mousemove fait l'affaire, mais dans le cas où l'utilisateur a cliqué et que:
... nous n'obtiendrons pas d'événement mousemove, donc pas de rappel d'annulation. De plus, si l'utilisateur annule la deuxième boîte de dialogue et déplace la souris, nous aurons deux annulations de rappel . Heureusement, un événement spécial jQuery focusIn bouillonne jusqu'au document dans les deux cas, nous évitant ainsi de telles situations. La seule limite est si on bloque l'événement focusIn.
Dans mon cas, je devais masquer le bouton de soumission pendant que les utilisateurs sélectionnaient des images.
Voici ce que je monte:
$(document).on('click', '#image-field', function(e) {
$('.submit-button').prop('disabled', true)
})
$(document).on('focus', '#image-field'), function(e) {
$('.submit-button').prop('disabled', false)
})
#image-field
est mon sélecteur de fichier. Lorsque quelqu'un clique dessus, je désactive le bouton d'envoi de formulaire. Le fait est que, lorsque la boîte de dialogue de fichier est fermée - peu importe, ils sélectionnent un fichier ou annulent - #image-field
a retrouvé le focus, alors je l'écoute pour cet événement.
METTRE &AGRAVE; JOUR
J'ai trouvé que cela ne fonctionne pas dans les safaris et les poltergeist/phantomjs. Prenez cette information en compte si vous souhaitez l’appliquer.
Ce qui suit semble fonctionner pour moi (sur le bureau, Windows):
var openFile = function (mimeType, fileExtension) {
var defer = $q.defer();
var uploadInput = document.createElement("input");
uploadInput.type = 'file';
uploadInput.accept = '.' + fileExtension + ',' + mimeType;
var hasActivated = false;
var hasChangedBeenCalled = false;
var hasFocusBeenCalled = false;
var focusCallback = function () {
if (hasActivated) {
hasFocusBeenCalled = true;
document.removeEventListener('focus', focusCallback, true);
setTimeout(function () {
if (!hasChangedBeenCalled) {
uploadInput.removeEventListener('change', changedCallback, true);
defer.resolve(null);
}
}, 300);
}
};
var changedCallback = function () {
uploadInput.removeEventListener('change', changedCallback, true);
if (!hasFocusBeenCalled) {
document.removeEventListener('focus', focusCallback, true);
}
hasChangedBeenCalled = true;
if (uploadInput.files.length === 1) {
//File picked
var reader = new FileReader();
reader.onload = function (e) {
defer.resolve(e.target.result);
};
reader.readAsText(uploadInput.files[0]);
}
else {
defer.resolve(null);
}
};
document.addEventListener('focus', focusCallback, true); //Detect cancel
uploadInput.addEventListener('change', changedCallback, true); //Detect when a file is picked
uploadInput.click();
hasActivated = true;
return defer.promise;
}
Cela utilise angularjs $ q mais vous devriez pouvoir le remplacer par tout autre cadre de promesse si nécessaire.
Testé sur IE11, Edge, Chrome, Firefox, mais cela ne semble pas fonctionner sur Chrome sur une tablette Android car il ne déclenche pas l'événement Focus.
Si vous avez déjà besoin de JQuery, cette solution fonctionnera peut-être (il s'agit exactement du même code dont j'avais besoin dans mon cas, bien que l'utilisation d'une promesse consiste simplement à le forcer à attendre que la sélection du fichier soit résolue):
await new Promise(resolve => {
const input = $("<input type='file'/>");
input.on('change', function() {
resolve($(this).val());
});
$('body').one('focus', '*', e => {
resolve(null);
e.stopPropagation();
});
input.click();
});
Voici ma solution, en utilisant le focus d'entrée de fichier (sans utiliser de minuteries)
var fileInputSelectionInitiated = false;
function fileInputAnimationStart() {
fileInputSelectionInitiated = true;
if (!$("#image-selector-area-icon").hasClass("fa-spin"))
$("#image-selector-area-icon").addClass("fa-spin");
if (!$("#image-selector-button-icon").hasClass("fa-spin"))
$("#image-selector-button-icon").addClass("fa-spin");
}
function fileInputAnimationStop() {
fileInputSelectionInitiated = false;
if ($("#image-selector-area-icon").hasClass("fa-spin"))
$("#image-selector-area-icon").removeClass("fa-spin");
if ($("#image-selector-button-icon").hasClass("fa-spin"))
$("#image-selector-button-icon").removeClass("fa-spin");
}
$("#image-selector-area-wrapper").click(function (e) {
$("#fileinput").focus();
$("#fileinput").click();
});
$("#preview-image-wrapper").click(function (e) {
$("#fileinput").focus();
$("#fileinput").click();
});
$("#fileinput").click(function (e) {
fileInputAnimationStart();
});
$("#fileinput").focus(function (e) {
fileInputAnimationStop();
});
$("#fileinput").change(function(e) {
// ...
}
Le champ de type file
-, frustré, ne répond pas à beaucoup d'événements (le flou serait ravissant). Je vois beaucoup de gens suggérer des solutions à change
et se faire voter à leur insu.
change
fonctionne, mais il a un défaut majeur (par rapport à ce que nous voulons devons réaliser).
Lorsque vous venez de charger une page contenant un champ de fichier, ouvrez la boîte et appuyez sur Annuler. Rien, frustrant, change .
Ce que j'ai choisi de faire est de charger dans un état bloqué.
section#after-image
dans mon cas, est masquée. Lorsque mon file field
change, un bouton de téléchargement apparaît. Une fois le téléchargement réussi, section#after-image
est affiché.change
est déclenché par cette annulation et je peux (et je le fais) masquer à nouveau le bouton de téléchargement jusqu'à ce qu'un fichier approprié soit sélectionné.J'ai eu la chance que cet état bloqué était déjà la conception de ma forme. Vous n'avez pas besoin d'utiliser le même style, vous avez simplement le bouton de téléchargement initialement masqué puis, lors de la modification, la définition d'un champ masqué ou d'une variable javascript sur un élément que vous pouvez surveiller lors de l'envoi.
J'ai essayé de changer la valeur des fichiers [0] avant l'interaction du champ. Cela n'a rien fait concernant onchange.
Donc, oui, change
fonctionne, au moins aussi bon que ce que nous allons obtenir. Le champ de fichier est sécurisé, pour des raisons évidentes, mais à la frustration de développeurs bien intentionnés.
Cela ne convient pas à mon objectif, mais vous pouvez peut-être, onclick
, charger une invite d'avertissement (et non une alert()
, car elle bloque le traitement des pages), et la masquer si une modification est déclenchée et que les fichiers [0] sont nuls. Si le changement n'est pas déclenché, la div reste dans son état.
// Utiliser le survol au lieu du flou
var fileInput = $("#fileInput");
if (fileInput.is(":hover") {
//open
} else {
}
Ceci est au mieux hacky, mais voici un exemple de travail de ma solution pour détecter si un utilisateur a téléchargé un fichier, et ne lui permettant de continuer que s’il a téléchargé un fichier.
Cacher fondamentalement les Continue
, Save
, Proceed
ou quel que soit votre bouton. Ensuite, dans le JavaScript, vous récupérez le nom du fichier. Si le nom de fichier n'a pas de valeur, n'indiquez pas le bouton Continue
. Si elle a une valeur, affichez le bouton. Cela fonctionne également s'ils téléchargent d'abord un fichier, puis tentent de télécharger un autre fichier et de cliquer sur Annuler.
Voici le code.
HTML:
<div class="container">
<div class="row">
<input class="file-input" type="file" accept="image/*" name="fileUpload" id="fileUpload" capture="camera">
<label for="fileUpload" id="file-upload-btn">Capture or Upload Photo</label>
</div>
<div class="row padding-top-two-em">
<input class="btn btn-success hidden" id="accept-btn" type="submit" value="Accept & Continue"/>
<button class="btn btn-danger">Back</button>
</div></div>
JavaScript:
$('#fileUpload').change(function () {
var fileName = $('#fileUpload').val();
if (fileName != "") {
$('#file-upload-btn').html(fileName);
$('#accept-btn').removeClass('hidden').addClass('show');
} else {
$('#file-upload-btn').html("Upload File");
$('#accept-btn').addClass('hidden');
}
});
CSS:
.file-input {
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
}
.file-input + label {
font-size: 1.25em;
font-weight: normal;
color: white;
background-color: blue;
display: inline-block;
padding: 5px;
}
.file-input:focus + label,
.file-input + label:hover {
background-color: red;
}
.file-input + label {
cursor: pointer;
}
.file-input + label * {
pointer-events: none;
}
Pour le CSS, cela consiste en grande partie à rendre le site Web et le bouton accessibles à tous. Style de votre bouton à ce que vous aimez.
Il existe un moyen astucieux de le faire (ajouter des rappels ou résoudre une implémentation différée/promise au lieu d'appels alert()
):
var result = null;
$('<input type="file" />')
.on('change', function () {
result = this.files[0];
alert('selected!');
})
.click();
setTimeout(function () {
$(document).one('mousemove', function () {
if (!result) {
alert('cancelled');
}
});
}, 1000);
Fonctionnement: lorsque la boîte de dialogue de sélection de fichier est ouverte, le document ne reçoit pas les événements du pointeur de la souris. Il y a un délai de 1000 ms pour permettre à la boîte de dialogue de s'afficher et de bloquer la fenêtre du navigateur. Vérifié dans Chrome et Firefox (Windows uniquement).
Mais ce n'est pas un moyen fiable de détecter les dialogues annulés, bien sûr. Bien que, pourrait améliorer certains comportements d'interface utilisateur pour vous.
Je suis arrivé ici en cherchant une solution pour le téléchargement de fichiers en utilisant une entrée cachée. C’est la raison la plus courante pour rechercher un moyen de détecter l’annulation d’une entrée de fichier (dialogue Ouvrir fichier -> si un fichier a été sélectionné, exécutez-le code, sinon ne fais rien), voici ma solution:
var fileSelectorResolve;
var fileSelector = document.createElement('input');
fileSelector.setAttribute('type', 'file');
fileSelector.addEventListener('input', function(){
fileSelectorResolve(this.files[0]);
fileSelectorResolve = null;
fileSelector.value = '';
});
function selectFile(){
if(fileSelectorResolve){
fileSelectorResolve();
fileSelectorResolve = null;
}
return new Promise(function(resolve){
fileSelectorResolve = resolve;
fileSelector.dispatchEvent(new MouseEvent('click'));
});
}
Notez que si aucun fichier n'a été sélectionné, la première ligne ne sera renvoyée qu'une seule fois selectFile()
est appelée à nouveau (ou si vous avez appelé fileSelectorResolve()
ailleurs).
async function logFileName(){
const file = await selectFile();
if(!file) return;
console.log(file.name);
}
Un autre exemple:
async function uploadFile(){
const file = await selectFile();
if(!file) return;
// ... make an ajax call here to upload the file ...
}
Eh bien, cela ne répond pas exactement à votre question. Mon hypothèse est la suivante: vous avez un scénario lorsque vous ajoutez une entrée de fichier et appelez la sélection de fichier, et si l'utilisateur clique sur annuler, vous supprimez simplement l'entrée.
Si tel est le cas, alors: Pourquoi ajouter une entrée de fichier vide?
Créez celui-ci à la volée, mais ajoutez-le à DOM uniquement quand il est rempli.
var fileInput = $("<input type='file' name='files' style='display: none' />");
fileInput.bind("change", function() {
if (fileInput.val() !== null) {
// if has value add it to DOM
$("#files").append(fileInput);
}
}).click();
Donc ici, je crée <input type = "file" /> à la volée, me lie à son événement de changement et appelle immédiatement click. On change se déclenche uniquement lorsque l'utilisateur sélectionne un fichier et clique sur OK, sinon l'entrée ne sera pas ajoutée à DOM et ne sera donc pas soumise.
Exemple de travail ici: https://jsfiddle.net/69g0Lxno/3/