web-dev-qa-db-fra.com

Comment détecter quand annuler est cliqué sur l'entrée de fichier?

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).

83
Ppp

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

35
Shiboe

Tu ne peux pas.

Le résultat de la boîte de dialogue de fichier n'est pas exposé au navigateur.

17
Oded
/* 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 */
    }
});
7
Rizky K.

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.

6
zvolkov

É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

5
AEQ

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>
5
loveJs

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
    }
});
3

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);
    };
});
3
superdario128

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. 

2
ransomweaver

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é.

2
Steve Brooker

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é.

Concept

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.

La mise en oeuvre

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.

Quelques détails supplémentaires

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.

Bureau

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.

Solutions alternatives

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.

Les navigateurs, s'il vous plaît ...Inutile de dire que c'est manifestement ridicule. Le fait que les pages Web ne reçoivent aucune notification indiquant qu'un élément critique de l'interface utilisateur a été modifié est tout simplement risible. C’est vraiment un bogue dans la spécification, car il n’a jamais été conçu pour une interface utilisateur de capture de média en plein écran, et le fait de ne pas déclencher l’événement "change" revient à techniquement à spéc.

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*.

2
Douglas Parker

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.

1
Kunal

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);
});
1

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:

  • fichier annulé ouvrir la boîte de dialogue avec la touche d'échappement (sans déplacer la souris), cliquer à nouveau pour ouvrir à nouveau la boîte de dialogue de fichier ...
  • a basculé le focus vers une autre application, qui est revenu dans la boîte de dialogue d'ouverture de fichier du navigateur et l'a fermée, puis ouvert à nouveau via la touche Entrée ou la touche espace ...

... 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.

1
KGA

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.

1
bonyiii

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.

1
Peter

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();

});

0
user2397355

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) {
    // ...
}

0
Yovav

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é. 

  • La partie suivante du formulaire, un 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é.
  • Si l'utilisateur charge, ouvre la boîte de dialogue de fichier, puis annule, il ne voit jamais le bouton de téléchargement.
  • Si l'utilisateur choisit un fichier, le bouton de téléchargement s'affiche. S'ils ouvrent ensuite la boîte de dialogue et annulent, l'événement 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.

0
Regular Joe

// Utiliser le survol au lieu du flou

var fileInput = $("#fileInput");

if (fileInput.is(":hover") { 

    //open
} else {

}
0
jsil

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.

0
maxshuty

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.

0
alx

Solution de sélection de fichier avec entrée masquée

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'));
   });
}

Exemple d'utilisation:

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 ...
}
0
potato

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/

0
Alexey