Come rilevare quando si fa clic su Annulla durante l'input del file?


112

Come posso rilevare quando l'utente cancella un input di file utilizzando un input di file html?

onChange mi consente di rilevare quando scelgono un file, ma vorrei anche sapere quando annullano (chiude la finestra di dialogo di scelta del file senza selezionare nulla).


Puoi anche rilevare se qualcuno sceglie il file e poi prova a sceglierne uno, ma annulla, e.target.files
rimarrai

Risposte:


45

Sebbene non sia una soluzione diretta, e anche negativa in quanto funziona solo (per quanto ho testato) con onfocus (che richiede un blocco degli eventi piuttosto limitante), puoi ottenerlo con quanto segue:

document.body.onfocus = function(){ /*rock it*/ }

La cosa bella di questo, è che puoi attaccarlo / scollegarlo in tempo con l'evento file, e sembra anche funzionare bene con input nascosti (un vantaggio preciso se stai usando una soluzione visiva per il tipo di input predefinito scadente = ' file'). Dopodiché, devi solo capire se il valore di input è cambiato.

Un esempio:

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

Guardalo in azione: http://jsfiddle.net/Shiboe/yuK3r/6/

Purtroppo, sembra funzionare solo sui browser webkit. Forse qualcun altro può capire la soluzione firefox / IE


Non volevo che l'evento focus si attivasse ogni volta, quindi ho voluto usare addEventListener("focus",fn,{once: true}). Tuttavia non sono riuscito a farlo sparare usando document.body.addEventListener.. Non so perché. Invece ho ottenuto lo stesso risultato con window.addEventListener.
WoodenKitty

3
Questo non funziona in Firefox e Edge per me. Tuttavia, l'ascolto per l' mousemoveevento window.document , oltre ad ascoltare focussulla windowsembra funzionare in tutto il mondo (almeno nei browser moderni, non mi importa di relitti passato decennio). Fondamentalmente, qualsiasi evento di interazione può essere utilizzato per questa attività che è bloccato da una finestra di dialogo di apertura del file aperta.
John Weisz

@WoodenKitty come hai implementato questo utilizzando addEventListener. Sto cercando di farlo in angolare e il document.body non funziona nemmeno per me
Terrance Jackson,

questo non funziona sui campi di input nascosti.
Sebastian il

20

Non puoi.

Il risultato della finestra di dialogo del file non viene esposto al browser.


È possibile verificare se il selettore di file è aperto o meno?
Ppp

1
@Ppp - No. Il browser non te lo dice.
Oded

6
"Il risultato della finestra di dialogo del file non è esposto al browser." Questo non è vero se la finestra di dialogo viene chiusa selezionando un file.
Trevor

3
La modifica attivata utilizzando la finestra di dialogo del file viene esposta al browser. Vedi, se non c'è nessun file selezionato, e nessun file è selezionato successivamente, allora non è cambiato nulla, quindi non changeviene inviato alcun evento.
John Weisz

1
Inoltre, se un file è già selezionato e si seleziona di nuovo lo stesso file, non changeviene inviato alcun evento neanche per quello.
Patrick Roberts

16

Quindi getterò il mio cappello in questa domanda da quando ho trovato una nuova soluzione. Ho un'app Web progressiva che consente agli utenti di acquisire foto e video e caricarli. Usiamo WebRTC quando possibile, ma torniamo ai selettori di file HTML5 per dispositivi con meno supporto * tosse Safari tosse *. Se stai lavorando specificamente su un'applicazione web mobile Android / iOS che utilizza la fotocamera nativa per acquisire direttamente foto / video, questa è la soluzione migliore che ho trovato.

Il nocciolo di questo problema è che quando la pagina viene caricata, fileè null, ma poi quando l'utente apre la finestra di dialogo e preme "Annulla", fileè ferma null, quindi non è "cambiato", quindi non viene attivato alcun evento di "modifica". Per i desktop, questo non è poi così male perché la maggior parte delle interfacce utente desktop non dipendono dal sapere quando viene richiamato un annullamento, ma le interfacce utente mobili che attivano la fotocamera per acquisire una foto / video dipendono molto dal sapere quando viene premuto un annullamento.

Inizialmente ho utilizzato l' document.body.onfocusevento per rilevare quando l'utente è tornato dal selettore di file e questo ha funzionato per la maggior parte dei dispositivi, ma iOS 11.3 lo ha interrotto poiché l'evento non è stato attivato.

Concetto

La mia soluzione a questo è * brivido * per misurare i tempi della CPU per determinare se la pagina è attualmente in primo piano o sullo sfondo. Sui dispositivi mobili, il tempo di elaborazione viene assegnato all'app attualmente in primo piano. Quando una telecamera è visibile, ruberà il tempo della CPU e ridurrà la priorità del browser. Tutto quello che dobbiamo fare è misurare quanto tempo di elaborazione viene assegnato alla nostra pagina, quando la fotocamera viene avviata il nostro tempo disponibile diminuirà drasticamente. Quando la videocamera viene chiusa (annullata o meno), il nostro tempo disponibile aumenta.

Implementazione

Possiamo misurare la temporizzazione della CPU utilizzando setTimeout()per invocare un callback in X millisecondi e quindi misurare quanto tempo ci è voluto per invocarlo effettivamente. Il browser non lo invocerà mai esattamente dopo X millisecondi, ma se è abbastanza vicino allora dobbiamo essere in primo piano. Se il browser è molto lontano (oltre 10 volte più lento di quanto richiesto), dobbiamo essere in background. Un'implementazione di base di questo è così:

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

L'ho testato sulla versione recente di iOS e Android, attivando la fotocamera nativa impostando gli attributi <input />sull'elemento.

<input type="file" accept="image/*" capture="camera" />
<input type="file" accept="video/*" capture="camcorder" />

In realtà funziona molto meglio di quanto mi aspettassi. Esegue 10 prove richiedendo che un timer venga richiamato in 25 millisecondi. Quindi misura il tempo effettivamente impiegato per invocare e se la media di 10 prove è inferiore a 50 millisecondi, presumiamo di dover essere in primo piano e la fotocamera è sparita. Se è maggiore di 50 millisecondi, dobbiamo essere ancora in background e continuare ad aspettare.

Alcuni dettagli aggiuntivi

Ho usato setTimeout()piuttosto che setInterval()perché quest'ultimo può accodare più invocazioni che vengono eseguite immediatamente dopo l'altra. Questo potrebbe aumentare drasticamente il rumore nei nostri dati, quindi sono rimasto fedele setTimeout()anche se è un po 'più complicato farlo.

Questi numeri particolari hanno funzionato bene per me, anche se ho visto almeno una volta il caso in cui lo spegnimento della fotocamera è stato rilevato prematuramente. Credo che ciò sia dovuto al fatto che la fotocamera potrebbe essere lenta da aprire e il dispositivo potrebbe eseguire 10 prove prima che diventi effettivamente in background. L'aggiunta di più prove o l'attesa di circa 25-50 millisecondi prima di avviare questa funzione può essere una soluzione alternativa.

Desktop

Sfortunatamente, questo non funziona davvero per i browser desktop. In teoria lo stesso trucco è possibile in quanto danno la priorità alla pagina corrente rispetto alle pagine in background. Tuttavia, molti desktop hanno risorse sufficienti per mantenere la pagina in esecuzione alla massima velocità anche in background, quindi questa strategia non funziona nella pratica.

Soluzioni alternative

Una soluzione alternativa che non molte persone menzionano che ho esplorato è stata quella di deridere a FileList. Iniziamo con nullin <input />e poi se l'utente apre la fotocamera e annulla, tornano a null, il che non è un cambiamento e nessun evento si attiverà. Una soluzione potrebbe essere quella di assegnare un file fittizio all'inizio della <input />pagina, quindi impostarlo sunull sarebbe una modifica che attiverebbe l'evento appropriato.

Sfortunatamente, non esiste un modo ufficiale per creare un FileList, e l' <input />elemento richiede un FileListin particolare e non accetterà nessun altro valore oltre null. Naturalmente, gli FileListoggetti non possono essere costruiti direttamente, a causa di qualche vecchio problema di sicurezza che a quanto pare non è nemmeno più rilevante. L'unico modo per impossessarsene di uno al di fuori di un <input />elemento è utilizzare un hack che copia-incolla i dati per simulare un evento degli appunti che può contenere unFileList oggetto (stai fondamentalmente fingendo un drag-and-drop-a-file-on -evento del tuo sito web). Questo è possibile in Firefox, ma non per iOS Safari, quindi non era fattibile per il mio caso d'uso particolare.

Browser, per favore ...

Inutile dire che questo è palesemente ridicolo. Il fatto che le pagine web non ricevano alcuna notifica che un elemento critico dell'interfaccia utente è cambiato è semplicemente ridicolo. Questo è davvero un bug nelle specifiche, poiché non è mai stato concepito per un'interfaccia utente di acquisizione multimediale a schermo intero e non è tecnicamente conforme alle specifiche l'attivazione dell'evento "modifica" .

Tuttavia , i fornitori di browser possono riconoscere la realtà di questo? Questo potrebbe essere risolto con un nuovo evento "fatto" che viene attivato anche quando non si verifica alcuna modifica, oppure potresti semplicemente attivare "modifica" comunque. Sì, sarebbe contro le specifiche, ma è banale per me deduplicare un evento di modifica sul lato JavaScript, ma fondamentalmente impossibile inventare il mio evento "fatto". Anche la mia soluzione è davvero solo euristica, se non offre garanzie sullo stato del browser.

Allo stato attuale, questa API è fondamentalmente inutilizzabile per i dispositivi mobili e penso che una modifica del browser relativamente semplice potrebbe rendere questo infinitamente più facile per gli sviluppatori web * si allontana dalla soap box *.



7

Quando selezioni un file e fai clic su Apri / Annulla, l' inputelemento dovrebbe perdere il focus aka blur. Supponendo che l'iniziale valuedi inputè vuota, qualsiasi valore non vuoto nel blurgestore indicherebbe un OK e un valore vuoto significherebbe un Annulla.

AGGIORNAMENTO: blurnon viene attivato quando inputè nascosto. Quindi non puoi usare questo trucco con caricamenti basati su IFRAME, a meno che tu non voglia visualizzare temporaneamente il file input.


3
In Firefox 10 beta e Chrome 16, blurnon viene attivato quando si seleziona un file. Non ho provato in altri browser.
Blaise

1
Non funziona: la sfocatura in ingresso viene attivata immediatamente dopo il clic. Chrome 63.0.3239.84 x64 su Win7.
Qwertiy

7
/* 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 */
    }
});

6
Hai controllato se la tua elsedichiarazione viene mai valutata?
jayarjo

per quanto ricordo quando ho scritto questa risposta, se l'utente seleziona un file e quindi riseleziona il file (ma poi lo cancella), verrà trattato come nessun file selezionato, uso solo alert sulla variabile selected_file_name per testarlo. comunque, non l'ho più testato da allora.
Rizky

4
Posso dire, dopo alcuni test su più browser, che Firefox non attiva un evento di modifica all'annullamento, né cancella i file esistenti selezionati se viene riaperto e Annulla cliccato la seconda volta. Strano, eh?
mix3d

7

La maggior parte di queste soluzioni non funziona per me.

Il problema è che non sai mai quale evento verrà attivato prima, vero clicko è change? Non puoi assumere alcun ordine, perché probabilmente dipende dall'implementazione del browser.

Almeno in Opera e Chrome (fine 2015) clickviene attivato appena prima di "riempire" l'input con i file, quindi non conoscerai mai la durata files.length != 0fino a quando non ritarderai clickl'attivazione dopo change.

Ecco il codice:

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

Se fai clic sulla pagina ... "Annulla clic" ... = | (Testato con Chrome)
Eduardo Lucio

5

Ascolta anche l'evento clic.

Seguendo l'esempio di Shiboe, ecco un esempio di 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!');
    }

});

Puoi vederlo in azione qui: http://jsfiddle.net/T3Vwz


1
Ho provato su Chrome 51, non funziona la prima volta che selezioni i file. La soluzione è aggiungere un ritardo: jsfiddle.net/7jfbmunh
Benoit Blanchon

Il tuo violino non ha funzionato per me - utilizzando Firefox 58.0.2
barrypicker

4

Puoi prendere l'annullamento se scegli lo stesso file di prima e fai clic su Annulla: in questo caso.

Puoi farlo in questo modo:

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

1
Thnkx @loveJs questo post mi ha davvero aiutato.
VPK

4
Non esattamente, se l'utente seleziona lo stesso file non è un annullamento = (
Tiago Peres França

13
onchange si attiva solo in caso di modifica. Quindi, se il file è lo stesso file del precedente, il listener onchange non si attiverà.
Shane

1
È possibile cancellare la selezione dell'input (input.value = '') dopo aver rilevato la prima modifica, in modo che la seconda volta in cui viene eseguita la modifica anche se l'utente seleziona di nuovo lo stesso file.
Chris

1
@CodeGems è consentito impostare a livello di codice input.value su una stringa vuota. Ma hai ragione che impostarlo su qualsiasi altro valore non è consentito.
Chris

4

Il modo più semplice è verificare se sono presenti file nella memoria temporanea. Se desideri ottenere l'evento di modifica ogni volta che l'utente fa clic sull'input del file, puoi attivarlo.

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

2

La soluzione di Shiboe sarebbe buona se funzionasse sul webkit mobile, ma non è così. Quello che posso inventare è aggiungere un listener di eventi mousemove a qualche oggetto dom nel momento in cui viene aperta la finestra di input del file, in questo modo:

 $('.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'evento mousemove è bloccato dalla pagina mentre la finestra di dialogo del file è aperta e quando è chiusa controlla se ci sono file nell'input del file. Nel mio caso desidero che un indicatore di attività blocchi le cose fino al caricamento del file, quindi desidero rimuovere il mio indicatore solo all'annullamento.

Tuttavia questo non risolve per i dispositivi mobili, poiché non c'è il mouse da spostare. La mia soluzione è tutt'altro che perfetta, ma penso che sia abbastanza buona.

       $('.upload-progress').bind('touchstart', function() {
        checkForFiles(this);
      });

Ora stiamo ascoltando un tocco sullo schermo per fare lo stesso controllo dei file. Sono abbastanza sicuro che il dito dell'utente verrà messo sullo schermo abbastanza rapidamente dopo aver annullato e chiuso questo indicatore di attività.

Si potrebbe anche semplicemente aggiungere l'indicatore di attività sull'evento di modifica dell'input del file, ma sui dispositivi mobili ci sono spesso alcuni secondi di ritardo tra la selezione dell'immagine e l'attivazione dell'evento di modifica, quindi è molto migliore UX per l'indicatore di attività da visualizzare al inizio del processo.


2

Ho trovato questo attributo, è ancora più semplice.

if ($('#selectedFile')[0].files.length > 1)
{
   // Clicked on 'open' with file
} else {
   // Clicked on 'cancel'
}

Ecco selectedFileun file input type=file.


Questa è una buona soluzione per me, tranne che dovrebbe essere> = 1
Bron Thulke

2

So che questa è una domanda molto vecchia ma, nel caso in cui aiuti qualcuno, ho scoperto che quando utilizzavo l'evento onmousemove per rilevare l'annullamento, era necessario testare due o più di questi eventi in un breve lasso di tempo. Questo perché singoli eventi onmousemove vengono generati dal browser (Chrome 65) ogni volta che il cursore viene spostato fuori dalla finestra di dialogo di selezione del file e ogni volta che viene spostato fuori dalla finestra principale e di nuovo dentro. Un semplice contatore di eventi di movimento del mouse accoppiato con un timeout di breve durata per azzerare il contatore ha funzionato a meraviglia.


1
Il primo paragrafo è una buona risposta, anche se potrebbe essere diviso in più uno per una migliore leggibilità. Ma queste due righe "SOAPBOX" lo rendono al limite di una Non-Risposta poiché le domande non appartengono alle risposte. Puoi riformulare / riformulare un po 'la tua risposta? Grazie.
Filnor

1

Nel mio caso ho dovuto nascondere il pulsante di invio mentre gli utenti selezionavano le immagini.

Questo è quello che mi viene in mente:

$(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è il mio selettore di file. Quando qualcuno fa clic su di esso, disattivo il pulsante di invio del modulo. Il punto è che quando la finestra di dialogo del file si chiude, non importa se selezionano un file o annullano#image-field tornato il focus, quindi ascolto quell'evento.

AGGIORNARE

Ho scoperto che questo non funziona in safari e poltergeist / phantomjs. Tieni in considerazione queste informazioni se desideri implementarle.


1

Combinando le soluzioni di Shiboe e alx, ho il codice più affidabile:

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

In genere, l'evento mousemove fa il trucco, ma nel caso in cui l'utente abbia fatto un clic e poi:

  • annullata la finestra di dialogo di apertura del file con il tasto Esc (senza muovere il mouse), fatto un altro clic preciso per aprire nuovamente la finestra di dialogo del file ...
  • spostato lo stato attivo su qualsiasi altra applicazione, che è tornato alla finestra di dialogo di apertura del file del browser e l'ha chiuso, quindi riaperto tramite invio o spazio ...

... non riceveremo l'evento mousemove, quindi non verrà annullata la richiamata. Inoltre, se l'utente annulla la seconda finestra di dialogo e fa un movimento del mouse, otterremo 2 callback di annullamento. Fortunatamente, un evento speciale jQuery focusIn bolle fino al documento in entrambi i casi, aiutandoci a evitare tali situazioni. L'unica limitazione è se si blocca l'evento focusIn.


Ho notato l' mousemoveevento documentdurante la selezione dei file nella finestra di dialogo del sistema operativo in Chrome 56.0.2924.87 su Ubuntu 16.10. Nessun problema, quando non si sposta il mouse o si utilizza solo la tastiera per selezionare il file. Ho dovuto sostituire mousemovecon keydownper rilevare l'annullamento il prima possibile, ma non "troppo presto", quando la finestra di dialogo era ancora aperta.
Ferdinand Prantl

1

Vedo che la mia risposta sarebbe abbastanza obsoleta, ma non di meno. Ho affrontato lo stesso problema. Quindi ecco la mia soluzione. Il codice più utile tagliato è stato quello di KGA. Ma non funziona completamente ed è un po 'complicato. Ma l'ho semplificato.

Inoltre, il problema principale è stato il fatto che l'evento di "cambiamento" non avviene immediatamente dopo il focus, quindi dobbiamo aspettare un po 'di tempo.

"#appendfile" - su cui l'utente fa clic per aggiungere un nuovo file. Gli hrefs ottengono eventi di 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

Puoi rilevarlo solo in circostanze limitate. In particolare, in Chrome se un file è stato selezionato in precedenza e poi si fa clic sulla finestra di dialogo del file e si fa clic su Annulla, Chrome cancella il file e attiva l'evento onChange.

https://code.google.com/p/chromium/issues/detail?id=2508

In questo scenario, è possibile rilevarlo gestendo l'evento onChange e controllando la proprietà dei file .


1

Quanto segue sembra funzionare per me (su desktop, 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;
}

Questo utilizza angularjs $ q ma dovresti essere in grado di sostituirlo con qualsiasi altro framework di promesse, se necessario.

Testato su IE11, Edge, Chrome, Firefox, ma non sembra funzionare su Chrome su un tablet Android in quanto non attiva l'evento Focus.


1

Il filecampo -type, frustrante, non risponde a molti eventi (la sfocatura sarebbe adorabile). Vedo molte persone che suggerisconochange soluzioni orientate e ottengono voti negativi.

changefunziona, ma ha un grosso difetto (rispetto a quello che vogliamo che accada).

Quando carichi di recente una pagina contenente un campo file, apri la casella e premi Annulla. Niente, frustrante, cambia .

Quello che ho scelto di fare è caricare in uno stato gated.

  • La parte successiva del modulo a section#after-imagenel mio caso è nascosta alla vista. Quando le mie file fieldmodifiche, viene visualizzato un pulsante di caricamento. Dopo il caricamento riuscito, section#after-imageviene visualizzato.
  • Se l'utente carica, apre la finestra di dialogo file, quindi annulla, non vedrà mai il pulsante di caricamento.
  • Se l'utente sceglie un file, viene visualizzato il pulsante di caricamento. Se quindi aprono la finestra di dialogo e annullano, l' changeevento viene attivato da questo annullamento e lì posso (e faccio) nascondere nuovamente il mio pulsante di caricamento finché non viene selezionato un file appropriato.

Sono stato fortunato che questo stato gated era già il progetto della mia forma. Non è necessario utilizzare lo stesso stile, basta avere il pulsante di caricamento inizialmente nascosto e al momento della modifica, impostare un campo nascosto o una variabile javascript su qualcosa che è possibile monitorare durante l'invio.

Ho provato a modificare il valore dei file [0] prima di interagire con il campo. Questo non ha fatto nulla per quanto riguarda onchange.

Quindi sì, change funziona, almeno tanto bene quanto avremo. Il filefield è protetto, per ovvie ragioni, ma con grande frustrazione per gli sviluppatori ben intenzionati.

Non è adatto al mio scopo, ma potresti essere in grado di onclickcaricare un prompt di avviso (non un messaggio alert(), perché blocca l'elaborazione della pagina) e nasconderlo se viene attivato il cambiamento e files [0] è nullo. Se la modifica non viene attivata, il div rimane nel suo stato.


0

C'è un modo hacker per farlo (aggiungere callback o risolvere alcune implementazioni differite / promesse invece di alert()chiamate):

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

Come funziona: mentre la finestra di selezione del file è aperta, il documento non riceve gli eventi del puntatore del mouse. C'è un ritardo di 1000 ms per consentire alla finestra di dialogo di apparire effettivamente e bloccare la finestra del browser. Controllato in Chrome e Firefox (solo Windows).

Ma questo non è un modo affidabile per rilevare il dialogo annullato, ovviamente. Tuttavia, potrebbe migliorare alcuni comportamenti dell'interfaccia utente per te.


Uno dei principali svantaggi è che su telefoni e tablet, non usano i mouse normalmente!
Peter,

0

Ecco la mia soluzione, utilizzando il focus di input del file (senza utilizzare alcun timer)

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


L'esecuzione del tuo snippet mi dàError: { "message": "Uncaught SyntaxError: missing ) after argument list", "filename": "https://stacksnippets.net/js", "lineno": 51, "colno": 1 }
collegamento

0

Questo è nella migliore delle ipotesi hacky, ma ecco un esempio funzionante della mia soluzione per rilevare se un utente ha caricato o meno un file e consentirgli di procedere solo se ha caricato un file.

Fondamentalmente nascondere il Continue, Save, Proceedo qualunque sia il vostro pulsante è. Quindi in JavaScript prendi il nome del file. Se il nome del file non ha un valore, non mostrare il Continuepulsante. Se ha un valore, mostra il pulsante. Funziona anche se all'inizio caricano un file e poi provano a caricare un file diverso e fanno clic su Annulla.

Ecco il codice.

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

Per il CSS, gran parte di questo è rendere il sito Web e il pulsante accessibili a tutti. Personalizza il tuo pulsante come preferisci.


0

Bene, questo non risponde esattamente alla tua domanda. La mia ipotesi è che, quando si aggiunge un input di file, si ha uno scenario e si richiama la selezione di file, e se l'utente preme su cancel, si rimuove semplicemente l'input.

Se questo è il caso, allora: Perché aggiungere un file di input vuoto?

Crea quello al volo, ma aggiungilo a DOM solo quando è compilato. In questo modo:

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

Quindi qui creo <input type = "file" /> al volo, mi associo al suo evento di modifica e quindi invoco immediatamente il clic. In caso di modifica si attiverà solo quando l'utente seleziona un file e preme Ok, altrimenti l'input non verrà aggiunto al DOM, quindi non verrà inviato.

Esempio di lavoro qui: https://jsfiddle.net/69g0Lxno/3/


0

// Usa il passaggio del mouse invece della sfocatura

var fileInput = $("#fileInput");

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

    //open
} else {

}

0

Se hai già bisogno di JQuery, questa soluzione potrebbe fare il lavoro (questo è esattamente lo stesso codice di cui avevo effettivamente bisogno nel mio caso, sebbene l'uso di una promessa sia solo per forzare il codice ad attendere fino a quando la selezione del file non è stata risolta):

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

Ci sono diverse soluzioni proposte in questo thread e questa difficoltà nel rilevare quando l'utente fa clic sul pulsante "Annulla" nella casella di selezione del file è un problema che riguarda molte persone.

Il fatto è che non esiste un modo affidabile al 100% per rilevare se l'utente ha fatto clic sul pulsante "Annulla" nella casella di selezione del file. Ma ci sono modi per rilevare in modo affidabile se l'utente ha aggiunto un file al file di input. Quindi questa è la strategia di base di questa risposta!

Ho deciso di aggiungere questa risposta perché a quanto pare le altre risposte non funzionano sulla maggior parte dei browser o garantite sui dispositivi mobili.

In breve il codice si basa su 3 punti:

  1. Il file di input viene inizialmente creato dinamicamente in "memoria" in js (al momento non lo aggiungiamo all '"HTML");
  2. Dopo aver aggiunto il file, il file di input viene aggiunto all'HTML, altrimenti non accade nulla;
  3. La rimozione del file viene eseguita rimuovendo il file di input dall'HTML tramite un evento specifico, il che significa che la "modifica" / "modifica" del file viene eseguita rimuovendo il vecchio file di input e creandone uno nuovo.

Per una migliore comprensione guarda il codice sottostante e anche le note.

[...]
<button type="button" onclick="addIptFl();">ADD INPUT FILE!</button>
<span id="ipt_fl_parent"></span>
[...]
function dynIptFl(jqElInst, funcsObj) {
    if (typeof funcsObj === "undefined" || funcsObj === "") {
        funcsObj = {};
    }
    if (funcsObj.hasOwnProperty("before")) {
        if (!funcsObj["before"].hasOwnProperty("args")) {
            funcsObj["before"]["args"] = [];
        }
        funcsObj["before"]["func"].apply(this, funcsObj["before"]["args"]);
    }
    var jqElInstFl = jqElInst.find("input[type=file]");

    // NOTE: Open the file selection box via js. By Questor
    jqElInstFl.trigger("click");

    // NOTE: This event is triggered if the user selects a file. By Questor
    jqElInstFl.on("change", {funcsObj: funcsObj}, function(e) {

        // NOTE: With the strategy below we avoid problems with other unwanted events
        // that may be associated with the DOM element. By Questor
        e.preventDefault();

        var funcsObj = e.data.funcsObj;
        if (funcsObj.hasOwnProperty("after")) {
            if (!funcsObj["after"].hasOwnProperty("args")) {
                funcsObj["after"]["args"] = [];
            }
            funcsObj["after"]["func"].apply(this, funcsObj["after"]["args"]);
        }
    });

}

function remIptFl() {

    // NOTE: Remove the input file. By Questor
    $("#ipt_fl_parent").empty();

}

function addIptFl() {

    function addBefore(someArgs0, someArgs1) {
    // NOTE: All the logic here happens just before the file selection box opens.
    // By Questor

        // SOME CODE HERE!

    }

    function addAfter(someArgs0, someArgs1) {
    // NOTE: All the logic here happens only if the user adds a file. By Questor

        // SOME CODE HERE!

        $("#ipt_fl_parent").prepend(jqElInst);

    }

    // NOTE: The input file is hidden as all manipulation must be done via js.
    // By Questor
    var jqElInst = $('\
<span>\
    <button type="button" onclick="remIptFl();">REMOVE INPUT FILE!</button>\
    <input type="file" name="input_fl_nm" style="display: block;">\
</span>\
');

    var funcsObj = {
        before: {
            func: addBefore,
            args: [someArgs0, someArgs1]
        },
        after: {
            func: addAfter,

            // NOTE: The instance with the input file ("jqElInst") could be passed
            // here instead of using the context of the "addIptFl()" function. That
            // way "addBefore()" and "addAfter()" will not need to be inside "addIptFl()",
            // for example. By Questor
            args: [someArgs0, someArgs1]

        }
    };
    dynIptFl(jqElInst, funcsObj);

}

Grazie! = D


0

Abbiamo ottenuto in angolare come sotto.

    1. bind evento clic sul file del tipo di input.
      1. Allega evento focus con finestra e aggiungi condizione se uploadPanel è true, quindi mostra console.
      2. quando si fa clic sul file del tipo di input, il valore booleano uploadPanel è vero. e appare la finestra di dialogo.
      3. quando si annulla o si fa clic sul pulsante Esc, vengono visualizzate la finestra di dialogo dispensario e console.

HTML

 <input type="file" formControlName="FileUpload" click)="handleFileInput($event.target.files)" />
          />

TS

this.uploadPanel = false;
handleFileInput(files: FileList) {
    this.fileToUpload = files.item(0);
    console.log("ggg" + files);
    this.uploadPanel = true;
  }



@HostListener("window:focus", ["$event"])
  onFocus(event: FocusEvent): void {
    if (this.uploadPanel == true) {
        console.log("cancel clicked")
        this.addSlot
        .get("FileUpload")
        .setValidators([
          Validators.required,
          FileValidator.validate,
          requiredFileType("png")
        ]);
      this.addSlot.get("FileUpload").updateValueAndValidity();
    }
  }

0

Aggiungi semplicemente l'ascoltatore "cambia" al tuo input il cui tipo è file. vale a dire

<input type="file" id="file_to_upload" name="file_to_upload" />

Ho usato jQuery e ovviamente chiunque può usare valina JS (come da requisito).

$("#file_to_upload").change(function() {

            if (this.files.length) {

            alert('file choosen');

            } else {

            alert('file NOT choosen');

            }

    });

.change()non viene chiamato in modo affidabile se l'utente fa clic su "Annulla" nel selettore di file.
Ben Wheeler

@benWheeler, l'ho controllato sia su Chrome che su Firefox e ha funzionato ma non testato su altri browser.
Naveed Ali il

0
    enter code here
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <h1>Hello</h1>
    <div id="cancel01">
      <button>Cancel</button>
    </div>

    <div id="cancel02">
      <button>Cancel</button>
    </div>

    <div id="cancel03">
      <button>Cancel</button>
    </div>

    <form>
      <input type="file" name="name" placeholder="Name" />
    </form>

    <script>
      const nameInput = document.querySelector('input[type="file"]');

      /******
       *The below code if for How to detect when cancel is clicked on file input
       ******/
      nameInput.addEventListener('keydown', e => {
        /******
         *If the cancel button is clicked,then you should change the input file value to empty
         ******/

        if (e.key == 'Backspace' || e.code == 'Backspace' || e.keyCode == 8) {
          console.log(e);

          /******
           *The below code will delete the file path
           ******/
          nameInput.value = '';
        }
      });
    </script>
  </body>
</html>

Le risposte di solo codice sono considerate di bassa qualità: assicurati di fornire una spiegazione su cosa fa il tuo codice e come risolve il problema. Aiuterà sia il richiedente che i futuri lettori se puoi aggiungere ulteriori informazioni nel tuo post. Vedi Spiegazione delle risposte interamente basate sul codice
Calos

0

Soluzione per la selezione di file con input nascosto

Nota: questo codice non rileva la cancellazione, offre un modo per aggirare la necessità di rilevarlo in un caso comune in cui le persone cercano di rilevarlo.

Sono arrivato qui mentre cercavo una soluzione per il caricamento di file utilizzando un input nascosto, credo che questo sia il motivo più comune per cercare un modo per rilevare la cancellazione dell'input di file (apri finestra di dialogo file -> se un file è stato selezionato, esegui alcuni codice, altrimenti non fare nulla), ecco la mia soluzione:

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

Esempio di utilizzo:

Nota che se non è stato selezionato alcun file, la prima riga tornerà solo una selectFile()volta (o se hai chiamato fileSelectorResolve()da altrove).

async function logFileName(){
   const file = await selectFile();
   if(!file) return;
   console.log(file.name);
}

Un altro esempio:

async function uploadFile(){
   const file = await selectFile();
   if(!file) return;

   // ... make an ajax call here to upload the file ...
}

In chrome 83.0, non ricevo alcuna richiamata per l'evento di input quando l'utente annulla, e nemmeno al 2 ° tentativo
Soylent Graham

1
@SoylentGraham Sì, poiché questo codice non rileva la cancellazione, offre un modo per aggirare la necessità di rilevarlo in un caso comune in cui le persone cercano di rilevarlo. Dato che non mi è stato chiaro, aggiungo questo commento alla mia risposta.
patata

Colpa mia, penso di aver letto male la tua risposta in quanto "ti dice che l'utente ha annullato il 2 ° tentativo"
Soylent Graham

-1

È possibile creare un listener di modifiche jquery nel campo di input e rilevare che l'utente annulla o ha chiuso la finestra di caricamento in base al valore del campo.

ecco un esempio:

//when upload button change
$('#upload_btn').change(function(){
    //get uploaded file
    var file = this.files[0];

    //if user choosed a file
    if(file){
        //upload file or perform your desired functiuonality
    }else{
        //user click cancel or close the upload window
    }
});

Non ho idea del motivo per cui questo è downvoted - questo è quello che stavo cercando. Grazie! 🙂
user1274820
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.