Risposte:
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
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
.
mousemove
evento window.document
, oltre ad ascoltare focus
sulla window
sembra 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.
Non puoi.
Il risultato della finestra di dialogo del file non viene esposto al browser.
change
viene inviato alcun evento.
change
viene inviato alcun evento neanche per quello.
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.onfocus
evento 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.
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.
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.
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.
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.
Una soluzione alternativa che non molte persone menzionano che ho esplorato è stata quella di deridere a FileList
. Iniziamo con null
in <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 FileList
in particolare e non accetterà nessun altro valore oltre null
. Naturalmente, gli FileList
oggetti 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.
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 *.
Quando selezioni un file e fai clic su Apri / Annulla, l' input
elemento dovrebbe perdere il focus aka blur
. Supponendo che l'iniziale value
di input
è vuota, qualsiasi valore non vuoto nel blur
gestore indicherebbe un OK e un valore vuoto significherebbe un Annulla.
AGGIORNAMENTO: blur
non 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
.
blur
non viene attivato quando si seleziona un file. Non ho provato in altri browser.
/* 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 */
}
});
else
dichiarazione viene mai valutata?
La maggior parte di queste soluzioni non funziona per me.
Il problema è che non sai mai quale evento verrà attivato prima, vero click
o è change
? Non puoi assumere alcun ordine, perché probabilmente dipende dall'implementazione del browser.
Almeno in Opera e Chrome (fine 2015) click
viene attivato appena prima di "riempire" l'input con i file, quindi non conoscerai mai la durata files.length != 0
fino a quando non ritarderai click
l'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);
};
});
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
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>
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
}
});
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.
Ho trovato questo attributo, è ancora più semplice.
if ($('#selectedFile')[0].files.length > 1)
{
// Clicked on 'open' with file
} else {
// Clicked on 'cancel'
}
Ecco selectedFile
un file input type=file
.
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.
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.
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:
... 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.
mousemove
evento document
durante 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 mousemove
con keydown
per rilevare l'annullamento il prima possibile, ma non "troppo presto", quando la finestra di dialogo era ancora aperta.
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);
});
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 .
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.
Il file
campo -type, frustrante, non risponde a molti eventi (la sfocatura sarebbe adorabile). Vedo molte persone che suggerisconochange
soluzioni orientate e ottengono voti negativi.
change
funziona, 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.
section#after-image
nel mio caso è nascosta alla vista. Quando le mie file field
modifiche, viene visualizzato un pulsante di caricamento. Dopo il caricamento riuscito, section#after-image
viene visualizzato.change
evento 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 onclick
caricare 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.
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.
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) {
// ...
}
Error: { "message": "Uncaught SyntaxError: missing ) after argument list", "filename": "https://stacksnippets.net/js", "lineno": 51, "colno": 1 }
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
, Proceed
o 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 Continue
pulsante. 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.
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/
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();
});
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:
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
Abbiamo ottenuto in angolare come sotto.
<input type="file" formControlName="FileUpload" click)="handleFileInput($event.target.files)" />
/>
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();
}
}
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.
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>
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'));
});
}
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 ...
}
È 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
}
});
e.target.files