Antworten:
Obwohl dies keine direkte Lösung ist und auch insofern schlecht, als es nur (soweit ich es getestet habe) mit Onfocus funktioniert (was eine ziemlich einschränkende Ereignisblockierung erfordert), können Sie dies mit den folgenden Ergebnissen erreichen:
document.body.onfocus = function(){ /*rock it*/ }
Das Schöne daran ist, dass Sie es rechtzeitig mit dem Dateiereignis anhängen / trennen können, und es scheint auch mit versteckten Eingaben gut zu funktionieren (ein klarer Vorteil, wenn Sie eine visuelle Problemumgehung für den beschissenen Standardeingabetyp verwenden = ' Datei'). Danach müssen Sie nur noch herausfinden, ob sich der Eingabewert geändert hat.
Ein Beispiel:
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')
}
Sehen Sie es in Aktion: http://jsfiddle.net/Shiboe/yuK3r/6/
Leider scheint es nur in Webkit-Browsern zu funktionieren. Vielleicht kann jemand anderes die Firefox / IE-Lösung herausfinden
addEventListener("focus",fn,{once: true})
. Ich konnte es jedoch überhaupt nicht zum Feuern bringen document.body.addEventListener
. Ich weiß nicht warum. Stattdessen habe ich das gleiche Ergebnis mit window.addEventListener
.
mousemove
auf Ereignis window.document
zusätzlich zu hören focus
auf window
scheint überall zu arbeiten (zumindest in modernen Browsern, ich kümmere mich nicht um Vergangenheits Jahrzehnt Wracks). Grundsätzlich kann für diese Aufgabe jedes Interaktionsereignis verwendet werden, das durch einen geöffneten Dialog zum Öffnen von Dateien blockiert wird.
Das kannst du nicht.
Das Ergebnis des Dateidialogs wird dem Browser nicht angezeigt.
change
Ereignis ausgelöst wird.
change
Ereignis ausgelöst.
Also werde ich meinen Hut in diese Frage werfen, da ich eine neuartige Lösung gefunden habe. Ich habe eine Progressive Web App, mit der Benutzer Fotos und Videos aufnehmen und hochladen können. Wir verwenden WebRTC, wenn möglich, greifen jedoch auf HTML5-Dateiauswahl für Geräte mit weniger Unterstützung zurück * Husten Safari Husten *. Wenn Sie speziell an einer mobilen Webanwendung für Android / iOS arbeiten, die die native Kamera verwendet, um Fotos / Videos direkt aufzunehmen, ist dies die beste Lösung, die mir begegnet ist.
Der Kern dieses Problems besteht darin, dass beim Laden der Seite das file
ist null
, aber wenn der Benutzer den Dialog öffnet und auf "Abbrechen" drückt, das file
ist immer noch null
, daher wurde es nicht "geändert", sodass kein "Ändern" -Ereignis ausgelöst wird. Für Desktops ist dies nicht schlecht, da die meisten Desktop-Benutzeroberflächen nicht davon abhängig sind, zu wissen, wann ein Abbruch aufgerufen wird. Mobile Benutzeroberflächen, die die Kamera zum Aufnehmen eines Fotos / Videos aufrufen, sind jedoch sehr davon abhängig, zu wissen, wann ein Abbruch gedrückt wird.
Ich habe das document.body.onfocus
Ereignis ursprünglich verwendet , um zu erkennen, wann der Benutzer von der Dateiauswahl zurückgekehrt ist. Dies funktionierte für die meisten Geräte, aber iOS 11.3 hat es unterbrochen, da dieses Ereignis nicht ausgelöst wird.
Meine Lösung hierfür ist * shudder *, um das CPU-Timing zu messen und festzustellen, ob sich die Seite derzeit im Vordergrund oder im Hintergrund befindet. Auf Mobilgeräten wird der aktuell im Vordergrund stehenden App Verarbeitungszeit zugewiesen. Wenn eine Kamera sichtbar ist, stiehlt sie die CPU-Zeit und setzt den Browser außer Kraft. Alles, was wir tun müssen, ist zu messen, wie viel Verarbeitungszeit unsere Seite hat, wenn die Kamera startet, wird unsere verfügbare Zeit drastisch sinken. Wenn die Kamera geschlossen wird (entweder abgebrochen oder auf andere Weise), wird unsere verfügbare Zeitspitze wieder erhöht.
Wir können das CPU-Timing messen, indem wir setTimeout()
einen Rückruf in X Millisekunden aufrufen und dann messen, wie lange es gedauert hat, ihn tatsächlich aufzurufen. Der Browser wird es nie genau nach X Millisekunden aufrufen , aber wenn es in der Nähe vernünftig ist, müssen wir im Vordergrund stehen. Wenn der Browser sehr weit entfernt ist (über 10x langsamer als angefordert), müssen wir uns im Hintergrund befinden. Eine grundlegende Implementierung davon ist wie folgt:
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);
});
}
Ich habe dies auf der neuesten Version von iOS und Android getestet und die native Kamera durch Festlegen der Attribute für das <input />
Element aufgerufen.
<input type="file" accept="image/*" capture="camera" />
<input type="file" accept="video/*" capture="camcorder" />
Das klappt eigentlich viel besser als ich erwartet hatte. Es werden 10 Versuche ausgeführt, indem angefordert wird, dass ein Timer in 25 Millisekunden aufgerufen wird. Anschließend wird gemessen, wie lange das Aufrufen tatsächlich gedauert hat. Wenn der Durchschnitt von 10 Versuchen weniger als 50 Millisekunden beträgt, gehen wir davon aus, dass wir im Vordergrund stehen müssen und die Kamera weg ist. Wenn es größer als 50 Millisekunden ist, müssen wir immer noch im Hintergrund sein und sollten weiter warten.
Ich habe setTimeout()
eher verwendet, als setInterval()
weil letztere mehrere Aufrufe in die Warteschlange stellen können, die unmittelbar nacheinander ausgeführt werden. Dies könnte das Rauschen in unseren Daten drastisch erhöhen, daher habe ich mich daran gehalten, setTimeout()
obwohl dies etwas komplizierter ist.
Diese bestimmten Zahlen haben bei mir gut funktioniert, obwohl ich mindestens einmal einen Fall gesehen habe, in dem die Kameraentlassung vorzeitig erkannt wurde. Ich glaube, das liegt daran, dass sich die Kamera möglicherweise nur langsam öffnet und das Gerät möglicherweise 10 Versuche ausführt, bevor es tatsächlich in den Hintergrund tritt. Das Hinzufügen weiterer Versuche oder das Warten von 25 bis 50 Millisekunden vor dem Starten dieser Funktion kann eine Problemumgehung sein.
Leider funktioniert dies bei Desktop-Browsern nicht wirklich. Theoretisch ist der gleiche Trick möglich, da die aktuelle Seite Vorrang vor Hintergrundseiten hat. Viele Desktops verfügen jedoch über genügend Ressourcen, um die Seite auch im Hintergrund mit voller Geschwindigkeit laufen zu lassen, sodass diese Strategie in der Praxis nicht wirklich funktioniert.
Eine alternative Lösung, die nicht viele Leute erwähnen, die ich erforscht habe, war das Verspotten von a FileList
. Wir beginnen mit null
in <input />
und wenn der Benutzer die Kamera öffnet und abbricht, kehren sie zurück null
, was keine Änderung darstellt und kein Ereignis auslöst. Eine Lösung wäre, dem <input />
Start der Seite eine Dummy-Datei zuzuweisen , daher null
wäre die Einstellung auf eine Änderung, die das entsprechende Ereignis auslösen würde.
Leider gibt es keine offizielle Möglichkeit, eine zu erstellen FileList
, und das <input />
Element erfordert eine FileList
bestimmte und akzeptiert keinen anderen Wert als diese null
. Natürlich können FileList
Objekte nicht direkt konstruiert werden, um ein altes Sicherheitsproblem zu lösen, das anscheinend nicht mehr relevant ist. Die einzige Möglichkeit, einen außerhalb eines <input />
Elements zu finden, besteht darin, einen Hack zu verwenden, der Daten kopiert und einfügt, um ein Zwischenablageereignis zu fälschen, das ein FileList
Objekt enthalten kann (Sie fälschen im Grunde ein Drag-and-Drop-A-File-On -Ihre-Website-Veranstaltung). Dies ist in Firefox möglich, jedoch nicht für iOS Safari. Daher war dies für meinen speziellen Anwendungsfall nicht möglich.
Unnötig zu sagen, dass dies offensichtlich lächerlich ist. Die Tatsache, dass Webseiten keine Benachrichtigung erhalten, dass sich ein kritisches UI-Element geändert hat, ist einfach lächerlich. Dies ist wirklich ein Fehler in der Spezifikation, da es nie für eine Vollbild-Benutzeroberfläche für die Medienerfassung gedacht war und das Nichtauslösen des "Änderungs" -Ereignisses technisch der Spezifikation entspricht.
Können Browser-Anbieter jedoch bitte die Realität erkennen? Dies könnte entweder mit einem neuen "erledigt" -Ereignis gelöst werden, das ausgelöst wird, auch wenn keine Änderung auftritt, oder Sie könnten sowieso nur "Änderung" auslösen. Ja, das wäre gegen die Spezifikation, aber es ist für mich trivial, ein Änderungsereignis auf der JavaScript-Seite zu dedupieren, aber grundsätzlich unmöglich, mein eigenes "erledigtes" Ereignis zu erfinden. Auch meine Lösung ist eigentlich nur Heuristik, wenn keine Garantie für den Zustand des Browsers besteht.
Derzeit ist diese API für mobile Geräte grundsätzlich unbrauchbar, und ich denke, eine relativ einfache Browseränderung könnte dies für Webentwickler unendlich einfacher machen * tritt aus der Seifenkiste *.
Wenn Sie eine Datei auswählen und auf Öffnen / Abbrechen klicken, sollte das input
Element den Fokus verlieren blur
. Angenommen, die Initiale value
von input
ist leer, würde jeder nicht leere Wert in Ihrem blur
Handler ein OK anzeigen, und ein leerer Wert würde Abbrechen bedeuten.
UPDATE: Das blur
wird nicht ausgelöst, wenn das input
ausgeblendet ist. Daher kann dieser Trick bei IFRAME-basierten Uploads nicht verwendet werden, es sei denn, Sie möchten das vorübergehend anzeigen input
.
blur
wird nicht ausgelöst, wenn Sie eine Datei auswählen. Ich habe es nicht in anderen Browsern versucht.
/* 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
Aussage jemals bewertet wird?
Die meisten dieser Lösungen funktionieren bei mir nicht.
Das Problem ist, dass Sie nie wissen, welches Ereignis zuerst ausgelöst wird, click
oder change
? Sie können keine Reihenfolge annehmen, da dies wahrscheinlich von der Implementierung des Browsers abhängt.
Zumindest in Opera und Chrome (Ende 2015) click
wird kurz vor dem "Füllen" der Eingabe mit Dateien ausgelöst, sodass Sie nie wissen, wie lange files.length != 0
es dauert click
, bis Sie die Auslösung verzögern change
.
Hier ist 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);
};
});
Hören Sie sich auch das Klickereignis an.
Nach Shiboes Beispiel ist hier ein jQuery-Beispiel:
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!');
}
});
Sie können es hier in Aktion sehen: http://jsfiddle.net/T3Vwz
Sie können den Abbruch abfangen, wenn Sie dieselbe Datei wie zuvor auswählen und auf Abbrechen klicken: in diesem Fall.
Sie können es so machen:
<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>
Am einfachsten ist es, zu überprüfen, ob sich Dateien im temporären Speicher befinden. Wenn Sie das Änderungsereignis jedes Mal erhalten möchten, wenn der Benutzer auf die Dateieingabe klickt, können Sie es auslösen.
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
}
});
Shiboes Lösung wäre gut, wenn sie mit einem mobilen Webkit funktionieren würde, aber nicht. Was ich mir einfallen lassen kann, ist, einem dom-Objekt zum Zeitpunkt des Öffnens des Dateieingabefensters einen Mausbewegungsereignis-Listener hinzuzufügen, wie folgt:
$('.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
};
Das Mousemove-Ereignis wird von der Seite blockiert, während der Dateidialog geöffnet ist. Wenn es geschlossen ist, wird überprüft, ob Dateien in der Dateieingabe vorhanden sind. In meinem Fall möchte ich, dass ein Aktivitätsindikator die Dinge blockiert, bis die Datei hochgeladen wird. Daher möchte ich meinen Indikator nur beim Abbrechen entfernen.
Dies lässt sich jedoch nicht für Mobilgeräte lösen, da keine Maus zum Bewegen vorhanden ist. Meine Lösung dort ist weniger als perfekt, aber ich denke, es ist gut genug.
$('.upload-progress').bind('touchstart', function() {
checkForFiles(this);
});
Jetzt warten wir auf eine Berührung auf dem Bildschirm, um die gleiche Dateiprüfung durchzuführen. Ich bin ziemlich sicher, dass der Finger des Benutzers nach dem Abbrechen und Deaktivieren dieser Aktivitätsanzeige ziemlich schnell auf dem Bildschirm angezeigt wird.
Man könnte auch einfach den Aktivitätsindikator zum Änderungsereignis für die Dateieingabe hinzufügen, aber auf Mobilgeräten gibt es oft einige Sekunden Verzögerung zwischen der Auswahl des Bildes und dem Auslösen des Änderungsereignisses, so dass die UX für den Aktivitätsindikator viel besser ist Beginn des Prozesses.
Ich fand dieses Attribut, das bisher einfachste.
if ($('#selectedFile')[0].files.length > 1)
{
// Clicked on 'open' with file
} else {
// Clicked on 'cancel'
}
Hier selectedFile
ist ein input type=file
.
Ich weiß, dass dies eine sehr alte Frage ist, aber nur für den Fall, dass sie jemandem hilft, stellte ich bei der Verwendung des Ereignisses onmousemove zum Erkennen des Abbruchs fest, dass es notwendig war, in kurzer Zeit zwei oder mehr solcher Ereignisse zu testen. Dies lag daran, dass der Browser (Chrome 65) jedes Mal, wenn der Cursor aus dem Dialogfenster für die Auswahldatei und wieder aus dem Hauptfenster heraus und wieder hinein bewegt wird, einzelne Onmousemove-Ereignisse generiert. Ein einfacher Zähler für Mausbewegungsereignisse In Verbindung mit einer kurzen Zeitüberschreitung, um den Zähler auf Null zurückzusetzen, war dies ein Vergnügen.
In meinem Fall musste ich die Schaltfläche "Senden" ausblenden, während Benutzer Bilder auswählten.
Das komme ich auf:
$(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
ist meine Dateiauswahl. Wenn jemand darauf klickt, deaktiviere ich die Schaltfläche zum Senden von Formularen. Der Punkt ist, wenn der Dateidialog geschlossen wird - egal, #image-field
ob sie eine Datei auswählen oder abbrechen -, den Fokus wieder erhalten hat, also höre ich mir dieses Ereignis an.
AKTUALISIEREN
Ich fand, dass dies bei Safari und Poltergeist / Phantomjs nicht funktioniert. Berücksichtigen Sie diese Informationen, wenn Sie sie implementieren möchten.
Durch die Kombination der Lösungen von Shiboe und alx habe ich den zuverlässigsten Code:
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 */
Im Allgemeinen reicht das Mausbewegungsereignis aus, aber falls der Benutzer einen Klick gemacht hat und dann:
... wir bekommen kein Mausbewegungsereignis, daher kein Rückruf abbrechen. Wenn der Benutzer den zweiten Dialog abbricht und eine Maus bewegt, werden zwei Rückrufe abgebrochen. Glücklicherweise sprudelt in beiden Fällen ein spezielles jQuery focusIn-Ereignis in das Dokument, um solche Situationen zu vermeiden. Die einzige Einschränkung ist, wenn man das focusIn-Ereignis blockiert.
mousemove
Ereignis document
beim Auswählen von Dateien im Dialogfeld "Betriebssystem" in Chrome 56.0.2924.87 unter Ubuntu 16.10 festgestellt. Kein Problem, wenn Sie die Maus nicht bewegen oder nur die Tastatur zur Auswahl der Datei verwenden. Ich hatte zu ersetzen mousemove
durch , keydown
um die so früh wie möglich aufheben zu erkennen, aber nicht „zu früh“, wenn der Dialog war noch offen.
Ich sehe, dass meine Antwort ziemlich veraltet wäre, aber trotzdem. Ich hatte das gleiche Problem. Hier ist meine Lösung. Der nützlichste Code war der von KGA. Aber es funktioniert nicht ganz und ist ein bisschen kompliziert. Aber ich habe es vereinfacht.
Der Hauptstörer war auch die Tatsache, dass das Änderungsereignis nicht sofort nach dem Fokussieren eintritt, sodass wir einige Zeit warten müssen.
"#appendfile" - welcher Benutzer klickt, um eine neue Datei anzuhängen. Hrefs erhalten Fokusereignisse.
$("#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);
});
Sie können dies nur unter bestimmten Umständen feststellen. Insbesondere in Chrome löscht Chrome die Datei und löst das Ereignis onChange aus, wenn zuvor eine Datei ausgewählt wurde und dann auf das Dateidialogfeld geklickt und auf Abbrechen geklickt wird.
https://code.google.com/p/chromium/issues/detail?id=2508
In diesem Szenario können Sie dies erkennen, indem Sie das Ereignis onChange behandeln und die Eigenschaft files überprüfen .
Folgendes scheint für mich zu funktionieren (auf 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;
}
Dies verwendet zwar anglejs $ q, aber Sie sollten es bei Bedarf durch ein anderes Versprechen-Framework ersetzen können.
Getestet auf IE11, Edge, Chrome, Firefox, aber es scheint nicht auf Chrome auf einem Android-Tablet zu funktionieren, da das Focus-Ereignis nicht ausgelöst wird.
Das file
Feld vom Typ "reagiert frustrierend nicht auf viele Ereignisse (Unschärfe wäre sehr schön). Ich sehe viele Leute, die change
orientierte Lösungen vorschlagen, und sie werden herabgestimmt.
change
funktioniert, aber es hat einen großen Fehler (im Vergleich zu dem, was wir wollen ).
Wenn Sie eine Seite mit einem Dateifeld neu laden, öffnen Sie das Feld und klicken Sie auf Abbrechen. Frustrierend ändert sich nichts .
Was ich gewählt habe, ist das Laden in einem geschlossenen Zustand.
section#after-image
in meinem Fall nicht sichtbar. Bei meinen file field
Änderungen wird eine Upload-Schaltfläche angezeigt. Nach erfolgreichem Upload section#after-image
wird angezeigt.change
Ereignis durch diesen Abbruch ausgelöst, und dort kann (und kann) ich meine Upload-Schaltfläche wieder ausblenden, bis eine richtige Datei ausgewählt ist.Ich hatte das Glück, dass dieser geschlossene Zustand bereits das Design meiner Form war. Sie müssen nicht denselben Stil verwenden, sondern lediglich die Schaltfläche zum Hochladen zunächst ausblenden und beim Ändern ein ausgeblendetes Feld oder eine Javascript-Variable auf etwas setzen, das Sie beim Senden überwachen können.
Ich habe versucht, den Wert von Dateien [0] zu ändern, bevor mit dem Feld interagiert wurde. Dies hat nichts in Bezug auf den Wechsel bewirkt.
Also ja, change
funktioniert, mindestens so gut wie wir es bekommen werden. Das Dateifeld ist aus offensichtlichen Gründen gesichert, aber zur Frustration gut gemeinter Entwickler.
Es passt nicht zu meinem Zweck, aber Sie können möglicherweise onclick
eine Warnmeldung laden (keine alert()
, da dies die Seitenverarbeitung blockiert) und sie ausblenden, wenn eine Änderung ausgelöst wird und files [0] null ist. Wenn keine Änderung ausgelöst wird, bleibt das div in seinem Zustand.
Es gibt eine hackige Möglichkeit, dies zu tun (Rückrufe hinzufügen oder eine verzögerte / versprochene Implementierung anstelle von alert()
Aufrufen auflösen ):
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);
So funktioniert es: Während der Dateiauswahldialog geöffnet ist, empfängt das Dokument keine Mauszeigerereignisse. Es gibt eine Verzögerung von 1000 ms, damit der Dialog tatsächlich angezeigt wird und das Browserfenster blockiert wird. Eingecheckt in Chrome und Firefox (nur Windows).
Dies ist natürlich keine zuverlässige Methode, um abgebrochene Dialoge zu erkennen. Dies könnte jedoch das Verhalten der Benutzeroberfläche für Sie verbessern.
Hier ist meine Lösung mit dem Dateieingabefokus (ohne 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 }
Dies ist bestenfalls hackig, aber hier ist ein funktionierendes Beispiel meiner Lösung, um festzustellen, ob ein Benutzer eine Datei hochgeladen hat oder nicht, und um ihm nur dann zu erlauben, fortzufahren, wenn er eine Datei hochgeladen hat.
Grundsätzlich verstecken die Continue
, Save
, Proceed
oder was auch immer Ihre Taste ist. Dann greifen Sie im JavaScript auf den Dateinamen zu. Wenn der Dateiname keinen Wert hat, wird die Continue
Schaltfläche nicht angezeigt. Wenn es einen Wert hat, zeigen Sie die Schaltfläche. Dies funktioniert auch, wenn sie zuerst eine Datei hochladen und dann versuchen, eine andere Datei hochzuladen und auf Abbrechen klicken.
Hier ist der 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;
}
Für das CSS bedeutet dies viel, die Website und die Schaltfläche für alle zugänglich zu machen. Gestalten Sie Ihren Button nach Ihren Wünschen.
Nun, das beantwortet Ihre Frage nicht genau. Ich gehe davon aus, dass Sie ein Szenario haben, in dem Sie eine Dateieingabe hinzufügen und die Dateiauswahl aufrufen. Wenn der Benutzer auf Abbrechen klickt, entfernen Sie einfach die Eingabe.
Wenn dies der Fall ist, dann: Warum leere Dateieingaben hinzufügen?
Erstellen Sie die Datei im laufenden Betrieb, fügen Sie sie jedoch nur dann zum DOM hinzu, wenn sie ausgefüllt ist.
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();
Also erstelle ich hier <input type = "file" /> im laufenden Betrieb, binde an das Änderungsereignis und rufe dann sofort click auf. Bei Änderung wird nur ausgelöst, wenn der Benutzer eine Datei auswählt und auf OK drückt. Andernfalls werden keine Eingaben zum DOM hinzugefügt und daher nicht gesendet.
Arbeitsbeispiel hier: https://jsfiddle.net/69g0Lxno/3/
Wenn Sie JQuery bereits benötigen, erledigt diese Lösung möglicherweise die Arbeit (dies ist genau der Code, den ich in meinem Fall tatsächlich benötigt habe, obwohl die Verwendung eines Versprechens nur dazu dient, den Code zu zwingen, zu warten, bis die Dateiauswahl aufgelöst wurde):
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();
});
In diesem Thread werden mehrere Lösungen vorgeschlagen, und diese Schwierigkeit, zu erkennen, wann der Benutzer auf die Schaltfläche "Abbrechen" im Dateiauswahlfeld klickt, ist ein Problem, das viele Menschen betrifft.
Tatsache ist, dass es keine 100% zuverlässige Methode gibt, um festzustellen, ob der Benutzer auf die Schaltfläche "Abbrechen" im Dateiauswahlfeld geklickt hat . Es gibt jedoch Möglichkeiten, zuverlässig zu erkennen, ob der Benutzer der Eingabedatei eine Datei hinzugefügt hat . Das ist also die Grundstrategie dieser Antwort!
Ich habe beschlossen, diese Antwort hinzuzufügen, da die anderen Antworten anscheinend in den meisten Browsern nicht funktionieren oder auf Mobilgeräten garantiert sind.
Kurz gesagt basiert der Code auf 3 Punkten:
Zum besseren Verständnis lesen Sie den folgenden Code und die Hinweise.
[...]
<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);
}
Vielen Dank! = D.
Wir haben in Winkel wie unten erreicht.
<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();
}
}
Fügen Sie einfach "Listener" zu Ihrer Eingabe hinzu, deren Typ Datei ist. dh
<input type="file" id="file_to_upload" name="file_to_upload" />
Ich habe jQuery verwendet und natürlich kann jeder Valina JS verwenden (gemäß den Anforderungen).
$("#file_to_upload").change(function() {
if (this.files.length) {
alert('file choosen');
} else {
alert('file NOT choosen');
}
});
.change()
wird nicht zuverlässig aufgerufen, wenn der Benutzer in der Dateiauswahl auf "Abbrechen" klickt.
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>
Hinweis: Dieser Code erkennt keine Stornierung. Er bietet eine Möglichkeit, die Notwendigkeit zu umgehen, ihn in einem häufigen Fall zu erkennen, in dem Personen versuchen, ihn zu erkennen.
Ich bin hierher gekommen, als ich nach einer Lösung für das Hochladen von Dateien mithilfe einer versteckten Eingabe gesucht habe. Ich glaube, dass dies der häufigste Grund ist, nach einer Möglichkeit zu suchen, die Stornierung der Dateieingabe zu erkennen (Dialogfeld "Datei öffnen" -> Wenn eine Datei ausgewählt wurde, führen Sie einige aus Code, sonst nichts tun), hier ist meine Lösung:
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'));
});
}
Beachten Sie, dass, wenn keine Datei ausgewählt wurde, die erste Zeile nur einmal zurückgegeben selectFile()
wird und erneut aufgerufen wird (oder wenn Sie fileSelectorResolve()
von einer anderen Stelle aus angerufen haben ).
async function logFileName(){
const file = await selectFile();
if(!file) return;
console.log(file.name);
}
Ein anderes Beispiel:
async function uploadFile(){
const file = await selectFile();
if(!file) return;
// ... make an ajax call here to upload the file ...
}
Sie können einen Listener für jquery change im Eingabefeld erstellen und anhand des Werts des Felds erkennen, dass der Benutzer das Upload-Fenster abbricht oder schließt.
Hier ist ein Beispiel:
//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