Jawaban:
Meskipun bukan solusi langsung, dan juga buruk karena hanya (sejauh yang saya uji) berfungsi dengan onfocus (memerlukan pemblokiran acara yang cukup membatasi) Anda dapat mencapainya dengan yang berikut:
document.body.onfocus = function(){ /*rock it*/ }
Yang menyenangkan tentang ini, adalah Anda dapat melampirkan / melepaskannya tepat waktu dengan acara file, dan tampaknya juga berfungsi dengan baik dengan input tersembunyi (keuntungan yang pasti jika Anda menggunakan solusi visual untuk tipe input default jelek = ' mengajukan'). Setelah itu, Anda hanya perlu mencari tahu apakah nilai masukannya berubah.
Sebuah contoh:
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')
}
Lihat aksinya: http://jsfiddle.net/Shiboe/yuK3r/6/
Sayangnya, ini sepertinya hanya berfungsi di browser webkit. Mungkin orang lain dapat mengetahui solusi firefox / IE
addEventListener("focus",fn,{once: true})
. Namun saya tidak bisa membuatnya menyala sama sekali menggunakan document.body.addEventListener
.. Saya tidak tahu mengapa. Sebaliknya saya mendapat hasil yang sama dengan window.addEventListener
.
mousemove
acara pada window.document
selain untuk mendengarkan focus
pada window
tampaknya bekerja di mana-mana (setidaknya di browser modern, saya tidak peduli tentang kecelakaan lalu-dekade). Pada dasarnya, setiap peristiwa interaksi dapat digunakan untuk tugas ini yang diblokir oleh dialog buka file yang terbuka.
Tidak boleh.
Hasil dari dialog file tidak diekspos ke browser.
change
peristiwa yang dikirim.
change
acara yang dikirim untuk itu juga.
Jadi saya akan melemparkan topi saya ke pertanyaan ini karena saya menemukan solusi baru. Saya memiliki Aplikasi Web Progresif yang memungkinkan pengguna untuk mengambil foto dan video dan mengunggahnya. Kami menggunakan WebRTC bila memungkinkan, tetapi kembali ke pemilih file HTML5 untuk perangkat dengan dukungan yang lebih sedikit * batuk Safari batuk *.Jika Anda bekerja secara khusus pada aplikasi web seluler Android / iOS yang menggunakan kamera asli untuk mengambil foto / video secara langsung, maka ini adalah solusi terbaik yang saya temukan.
Inti dari masalah ini adalah ketika halaman dimuat, file
is null
, tetapi kemudian ketika pengguna membuka dialog dan menekan "Batal", file
itu diam null
, maka itu tidak "berubah", jadi tidak ada peristiwa "perubahan" yang dipicu. Untuk desktop, ini tidak terlalu buruk karena sebagian besar UI desktop tidak bergantung pada mengetahui kapan pembatalan dipanggil, tetapi UI seluler yang memunculkan kamera untuk mengambil foto / video sangat bergantung pada mengetahui kapan pembatalan ditekan.
Saya awalnya menggunakan document.body.onfocus
acara tersebut untuk mendeteksi ketika pengguna kembali dari pemilih file, dan ini berfungsi untuk sebagian besar perangkat, tetapi iOS 11.3 merusaknya karena acara itu tidak dipicu.
Solusi saya untuk ini adalah * ngeri * untuk mengukur waktu CPU untuk menentukan apakah halaman saat ini berada di latar depan atau latar belakang. Di perangkat seluler, waktu pemrosesan diberikan ke aplikasi yang saat ini berada di latar depan. Saat kamera terlihat, itu akan mencuri waktu CPU dan menurunkan prioritas browser. Yang perlu kita lakukan adalah mengukur berapa banyak waktu pemrosesan yang diberikan halaman kita, ketika kamera diluncurkan, waktu yang tersedia akan turun secara drastis. Saat kamera ditutup (baik dibatalkan atau tidak), waktu yang tersedia melonjak kembali.
Kita dapat mengukur waktu CPU dengan menggunakan setTimeout()
untuk memanggil callback dalam X milidetik, lalu mengukur berapa lama waktu yang dibutuhkan untuk benar-benar memanggilnya. Browser tidak akan pernah memanggilnya tepat setelah X milidetik, tetapi jika ditutup secara wajar maka kita harus berada di latar depan. Jika browser sangat jauh (lebih dari 10x lebih lambat dari yang diminta) maka kita harus berada di latar belakang. Implementasi dasar dari ini seperti:
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);
});
}
Saya menguji ini pada versi terbaru iOS dan Android, memunculkan kamera asli dengan mengatur atribut pada <input />
elemen.
<input type="file" accept="image/*" capture="camera" />
<input type="file" accept="video/*" capture="camcorder" />
Ini berhasil sebenarnya jauh lebih baik dari yang saya harapkan. Ini menjalankan 10 percobaan dengan meminta timer untuk dipanggil dalam 25 milidetik. Kemudian mengukur berapa lama waktu yang dibutuhkan untuk memanggil, dan jika rata-rata 10 uji coba kurang dari 50 milidetik, kami berasumsi bahwa kami harus berada di latar depan dan kamera hilang. Jika lebih dari 50 milidetik, maka kita harus tetap berada di latar belakang dan harus terus menunggu.
Saya menggunakan setTimeout()
daripada setInterval()
karena yang terakhir dapat mengantri beberapa pemanggilan yang dieksekusi segera setelah satu sama lain. Hal ini secara drastis dapat meningkatkan gangguan dalam data kami, jadi saya tetap menggunakannya setTimeout()
meskipun sedikit lebih rumit untuk melakukannya.
Angka-angka tertentu ini bekerja dengan baik untuk saya, meskipun saya telah melihat setidaknya satu kali contoh di mana penutupan kamera terdeteksi sebelum waktunya. Saya yakin ini karena kamera mungkin lambat untuk dibuka, dan perangkat dapat menjalankan 10 uji coba sebelum benar-benar menjadi latar belakang. Menambahkan lebih banyak uji coba atau menunggu sekitar 25-50 milidetik sebelum memulai fungsi ini mungkin bisa menjadi solusi untuk itu.
Sayangnya, ini tidak benar-benar berfungsi untuk browser desktop. Secara teori, trik yang sama dimungkinkan karena mereka memprioritaskan halaman saat ini daripada halaman latar belakang. Namun banyak desktop memiliki sumber daya yang cukup untuk menjaga halaman tetap berjalan dengan kecepatan penuh bahkan saat di-background, jadi strategi ini tidak benar-benar berfungsi dalam praktiknya.
Salah satu solusi alternatif yang tidak banyak disebutkan orang yang saya eksplorasi adalah mengejek a FileList
. Kita mulai dengan null
di <input />
dan kemudian jika pengguna membuka kamera dan membatalkan mereka kembali null
, yang bukan merupakan perubahan dan tidak ada peristiwa yang akan dipicu. Salah satu solusinya adalah menetapkan file dummy ke <input />
awal halaman, oleh karena itu menyetel kenull
akan menjadi perubahan yang akan memicu peristiwa yang sesuai.
Sayangnya, tidak ada cara resmi untuk membuat a FileList
, dan <input />
elemen tersebut memerlukan FileList
khususnya dan tidak akan menerima nilai lain selain itu null
. Secara alami, FileList
objek tidak dapat dibangun secara langsung, lakukan untuk beberapa masalah keamanan lama yang tampaknya tidak relevan lagi. Satu-satunya cara untuk mendapatkan salah satu di luar <input />
elemen adalah dengan memanfaatkan peretasan yang menyalin-tempel data untuk memalsukan acara clipboard yang dapat berisi FileList
objek (Anda pada dasarnya memalsukan drag-and-drop-a-file-on acara situs web Anda). Ini dimungkinkan di Firefox, tetapi tidak untuk iOS Safari, jadi tidak layak untuk kasus penggunaan khusus saya.
Tak perlu dikatakan, ini benar-benar konyol. Fakta bahwa halaman web tidak diberi pemberitahuan bahwa elemen UI kritis telah berubah sungguh menggelikan. Ini benar-benar bug dalam spesifikasi, karena tidak pernah dimaksudkan untuk UI tangkapan media layar penuh, dan tidak memicu peristiwa "ubah" secara teknis ke spesifikasi.
Namun , dapatkah vendor browser mengenali kenyataan ini? Ini dapat diselesaikan dengan peristiwa "selesai" baru yang dipicu bahkan ketika tidak ada perubahan yang terjadi, atau Anda bisa saja memicu "perubahan". Ya, itu akan bertentangan dengan spesifikasi, tapi itu sepele bagi saya untuk menghapus peristiwa perubahan di sisi JavaScript, namun pada dasarnya tidak mungkin untuk menciptakan acara "selesai" saya sendiri. Bahkan solusi saya benar-benar hanya heuristik, jika tidak ada jaminan tentang status browser.
Seperti berdiri, API ini pada dasarnya tidak dapat digunakan untuk perangkat seluler, dan menurut saya perubahan browser yang relatif sederhana dapat membuat ini jauh lebih mudah bagi pengembang web * keluar dari kotak sabun *.
Saat Anda memilih file dan mengklik buka / batal, input
elemen akan kehilangan fokus alias blur
. Dengan asumsi awal value
dari input
kosong, setiap nilai yang kosong di Anda blur
handler akan mengindikasikan OK, dan nilai kosong berarti Batal.
UPDATE: Tidak blur
dipicu saat input
tersembunyi. Jadi tidak dapat menggunakan trik ini dengan unggahan berbasis IFRAME, kecuali Anda ingin menampilkan file input
.
blur
tidak dipicu saat Anda memilih file. Belum mencoba di browser lain.
/* 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
pernyataan Anda pernah dievaluasi?
Sebagian besar solusi ini tidak berhasil untuk saya.
Masalahnya adalah Anda tidak pernah tahu peristiwa mana yang akan memicu tinju, apakah itu click
atau apakah itu change
? Anda tidak dapat mengasumsikan urutan apa pun, karena mungkin tergantung pada implementasi browser.
Setidaknya di Opera dan Chrome (akhir 2015) click
dipicu tepat sebelum 'mengisi' input dengan file, jadi Anda tidak akan pernah tahu berapa lama files.length != 0
sampai Anda menunda click
untuk dipicu setelahnya change
.
Ini kodenya:
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);
};
});
Dengarkan acara klik juga.
Mengikuti dari contoh Shiboe, berikut adalah contoh 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!');
}
});
Anda dapat melihatnya beraksi di sini: http://jsfiddle.net/T3Vwz
Anda dapat menangkap pembatalan jika Anda memilih file yang sama seperti sebelumnya dan Anda mengklik batal: dalam kasus ini.
Anda bisa melakukannya seperti ini:
<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>
Cara termudah adalah dengan memeriksa apakah ada file di memori sementara. Jika Anda ingin mendapatkan peristiwa perubahan setiap kali pengguna mengklik input file, Anda dapat memicunya.
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
}
});
Solusi Shiboe akan bagus jika berfungsi di webkit seluler, tetapi tidak. Yang bisa saya dapatkan adalah menambahkan pendengar acara mousemove ke beberapa objek dom pada saat jendela input file dibuka, seperti:
$('.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
};
Peristiwa mousemove diblokir dari halaman saat dialog file terbuka, dan ketika ditutup, pemeriksaan untuk melihat apakah ada file dalam input file. Dalam kasus saya, saya ingin indikator aktivitas memblokir hal-hal sampai file diunggah, jadi saya hanya ingin menghapus indikator saya saat membatalkan.
Namun ini tidak menyelesaikan untuk seluler, karena tidak ada mouse untuk bergerak. Solusi saya di sana kurang sempurna, tapi menurut saya cukup bagus.
$('.upload-progress').bind('touchstart', function() {
checkForFiles(this);
});
Sekarang kami mendengarkan sentuhan di layar untuk melakukan pemeriksaan file yang sama. Saya cukup yakin bahwa jari pengguna akan diletakkan di layar dengan cukup cepat setelah membatalkan dan menutup indikator aktivitas ini.
Anda juga bisa menambahkan indikator aktivitas pada acara perubahan input file, tetapi pada perangkat seluler sering kali ada jeda beberapa detik antara memilih gambar dan pengaktifan acara perubahan, jadi UX yang jauh lebih baik untuk indikator aktivitas yang akan ditampilkan di memulai proses.
Saya menemukan atribut ini, yang paling sederhana.
if ($('#selectedFile')[0].files.length > 1)
{
// Clicked on 'open' with file
} else {
// Clicked on 'cancel'
}
Ini, selectedFile
adalah input type=file
.
Saya tahu ini adalah pertanyaan yang sangat lama tetapi untuk berjaga-jaga jika itu membantu seseorang, saya menemukan saat menggunakan acara onmousemove untuk mendeteksi pembatalan, bahwa perlu untuk menguji dua atau lebih acara semacam itu dalam waktu singkat. Ini karena peristiwa pemindahan tunggal satu kali dihasilkan oleh browser (Chrome 65) setiap kali kursor dipindahkan keluar dari jendela dialog file yang dipilih dan setiap kali dipindahkan dari jendela utama dan masuk kembali. Penghitung sederhana peristiwa gerakan mouse ditambah dengan batas waktu durasi singkat untuk mengatur ulang penghitung kembali ke nol bekerja dengan baik.
Dalam kasus saya, saya harus menyembunyikan tombol kirim saat pengguna memilih gambar.
Inilah yang saya dapatkan:
$(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
adalah pemilih file saya. Ketika seseorang mengkliknya, saya menonaktifkan tombol pengiriman formulir. Intinya adalah, ketika dialog file ditutup - tidak masalah mereka memilih file atau membatalkan -#image-field
mendapatkan fokus kembali, jadi saya mendengarkan acara itu.
MEMPERBARUI
Saya menemukan bahwa, ini tidak berfungsi di safari dan poltergeist / phantomjs. Pertimbangkan info ini jika Anda ingin menerapkannya.
Menggabungkan solusi Shiboe dan alx, saya mendapatkan kode yang paling andal:
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 */
Umumnya, acara mousemove melakukan trik, tetapi jika pengguna melakukan klik dan dari:
... kami tidak akan mendapatkan acara mousemove maka tidak ada panggilan balik pembatalan. Selain itu, jika pengguna membatalkan dialog kedua dan membuat mouse bergerak, kita akan mendapatkan 2 panggilan balik pembatalan. Untungnya, peristiwa jQuery focusIn khusus menggelembung ke dokumen dalam kedua kasus, membantu kita menghindari situasi seperti itu. Satu-satunya batasan adalah jika seseorang memblokir acara focusIn.
mousemove
acara tersebut document
saat memilih file dalam dialog OS di Chrome 56.0.2924.87 di Ubuntu 16.10. Tidak masalah, saat tidak menggerakkan mouse, atau hanya menggunakan keyboard untuk memilih file. Saya harus mengganti mousemove
by keydown
untuk mendeteksi pembatalan sedini mungkin, tetapi tidak "terlalu dini", saat dialog masih terbuka.
Saya melihat bahwa tanggapan saya akan sangat ketinggalan zaman, tetapi tidak pernah berkurang. Saya menghadapi masalah yang sama. Jadi inilah solusi saya. Potongan kode yang paling berguna adalah kode KGA. Tapi itu tidak sepenuhnya berfungsi dan agak rumit. Tapi saya menyederhanakannya.
Selain itu, pembuat masalah utama adalah fakta bahwa peristiwa 'perubahan' tidak datang langsung setelah fokus, jadi kita harus menunggu beberapa saat.
"#appendfile" - yang diklik pengguna untuk menambahkan file baru. Hrefs mendapatkan acara fokus.
$("#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);
});
Anda hanya dapat mendeteksi ini dalam keadaan terbatas. Secara khusus, di chrome jika file dipilih sebelumnya dan kemudian dialog file diklik dan batal diklik, Chrome menghapus file dan mengaktifkan peristiwa onChange.
https://code.google.com/p/chromium/issues/detail?id=2508
Dalam skenario ini, Anda dapat mendeteksi ini dengan menangani peristiwa onChange dan memeriksa properti file .
Hal berikut tampaknya berhasil untuk saya (di 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;
}
Ini memang menggunakan angularjs $ q tetapi Anda harus dapat menggantinya dengan framework promise lainnya jika diperlukan.
Diuji di IE11, Edge, Chrome, Firefox, tetapi tampaknya tidak berfungsi di Chrome pada Tablet Android karena tidak mengaktifkan acara Focus.
Bidang file
-type, dengan frustrasi, tidak merespons banyak peristiwa (blur akan menyenangkan). Saya melihat banyak orang menyarankanchange
solusi berorientasi dan mereka mendapatkan suara negatif.
change
berhasil, tetapi memiliki kelemahan utama (vs apa yang kita inginkan terjadi).
Saat Anda baru memuat halaman yang berisi bidang file, buka kotak dan tekan batal. Tidak ada, yang membuat frustrasi, berubah .
Apa yang saya pilih untuk dilakukan adalah memuat dalam keadaan terjaga keamanannya.
section#after-image
dalam kasus saya tersembunyi dari pandangan. Saat saya file field
berubah, tombol unggah ditampilkan. Setelah unggahan berhasil, section#after-image
ditampilkan.change
peristiwa tersebut dipicu oleh pembatalan ini, dan di sana saya dapat (dan melakukan) menyembunyikan kembali tombol unggah saya hingga file yang sesuai dipilih.Saya beruntung karena kondisi gerbang ini sudah menjadi desain bentuk saya. Anda tidak perlu menggunakan gaya yang sama, cukup dengan menyembunyikan tombol unggah dan setelah diubah, menyetel bidang tersembunyi atau variabel javascript ke sesuatu yang dapat Anda pantau saat dikirim.
Saya mencoba mengubah nilai file [0] sebelum bidang itu berinteraksi. Ini tidak melakukan apa-apa tentang onchange.
Jadi ya, change
berhasil, setidaknya sebaik yang akan kita dapatkan. Filefield diamankan, untuk alasan yang jelas, tetapi membuat frustrasi pengembang yang bermaksud baik.
Ini tidak sesuai dengan tujuan saya, tetapi Anda mungkin bisa onclick
,, memuat prompt peringatan (bukan alert()
, karena itu menghambat pemrosesan halaman), dan menyembunyikannya jika perubahan dipicu dan file [0] bernilai null. Jika perubahan tidak dipicu, div tetap dalam statusnya.
Ada cara hackish untuk melakukan ini (menambahkan callback atau menyelesaikan beberapa implementasi yang ditangguhkan / janji alih-alih alert()
panggilan):
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);
Cara kerjanya: saat dialog pemilihan file terbuka, dokumen tidak menerima peristiwa penunjuk mouse. Ada penundaan 1000ms untuk memungkinkan dialog benar-benar muncul dan memblokir jendela browser. Dicentang di Chrome dan Firefox (khusus Windows).
Tapi ini bukan cara yang dapat diandalkan untuk mendeteksi dialog yang dibatalkan, tentunya. Padahal, mungkin meningkatkan beberapa perilaku UI untuk Anda.
Inilah solusi saya, menggunakan fokus input file (tidak menggunakan timer apa pun)
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 }
Ini paling tidak hacky, tetapi berikut adalah contoh yang berfungsi dari solusi saya untuk mendeteksi apakah pengguna telah mengunggah file atau tidak, dan hanya mengizinkan mereka untuk melanjutkan jika mereka telah mengunggah file.
Pada dasarnya menyembunyikan Continue
, Save
, Proceed
atau apa pun tombol Anda. Kemudian di JavaScript Anda mengambil nama file. Jika nama file tidak memiliki nilai, maka jangan tampilkan Continue
tombolnya. Jika memang memiliki nilai, maka tunjukkan tombolnya. Ini juga berfungsi jika mereka pada awalnya mengunggah file dan kemudian mereka mencoba mengunggah file yang berbeda dan mengeklik batal.
Ini kodenya.
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;
}
Untuk CSS, banyak hal ini untuk membuat situs web dan tombol dapat diakses oleh semua orang. Atur gaya tombol Anda sesuai keinginan.
Nah, ini tidak benar-benar menjawab pertanyaan Anda. Asumsi saya adalah, Anda memiliki skenario, ketika Anda menambahkan input file, dan menjalankan pemilihan file, dan jika pengguna menekan batal, Anda cukup menghapus input tersebut.
Jika demikian, maka: Mengapa menambahkan input file kosong?
Buat satu dengan cepat, tetapi tambahkan ke DOM hanya jika sudah diisi. Seperti ini:
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();
Jadi di sini saya membuat <input type = "file" /> dengan cepat, mengikat ke acara perubahannya dan kemudian segera memanggil klik. On change akan aktif hanya ketika pengguna memilih file dan menekan Ok, jika tidak, input tidak akan ditambahkan ke DOM, oleh karena itu tidak akan dikirimkan.
Contoh kerja di sini: https://jsfiddle.net/69g0Lxno/3/
Jika Anda sudah memerlukan JQuery, solusi ini mungkin berhasil (ini adalah kode yang sama persis yang saya butuhkan dalam kasus saya, meskipun menggunakan Promise hanya untuk memaksa kode menunggu hingga pemilihan file diselesaikan):
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();
});
Ada beberapa solusi yang diusulkan di utas ini dan kesulitan untuk mendeteksi saat pengguna mengklik tombol "Batal" pada kotak pemilihan file merupakan masalah yang mempengaruhi banyak orang.
Faktanya adalah bahwa tidak ada cara yang 100% dapat diandalkan untuk mendeteksi jika pengguna telah mengklik tombol "Batal" pada kotak pemilihan file. Tetapi ada cara untuk mendeteksi dengan andal jika pengguna telah menambahkan file ke file input. Jadi inilah strategi dasar dari jawaban ini!
Saya memutuskan untuk menambahkan jawaban ini karena ternyata jawaban lain tidak berfungsi di sebagian besar browser atau dijamin di perangkat seluler.
Secara singkat kode ini didasarkan pada 3 poin:
Untuk pemahaman yang lebih baik, lihat kode di bawah ini dan juga catatannya.
[...]
<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);
}
Terima kasih! = D
Kami mencapai sudut seperti di bawah ini.
<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();
}
}
Cukup tambahkan pendengar 'ubah' pada masukan Anda yang tipenya adalah file. yaitu
<input type="file" id="file_to_upload" name="file_to_upload" />
Saya telah selesai menggunakan jQuery dan jelas siapa pun dapat menggunakan valina JS (sesuai kebutuhan).
$("#file_to_upload").change(function() {
if (this.files.length) {
alert('file choosen');
} else {
alert('file NOT choosen');
}
});
.change()
tidak dapat dipanggil secara andal jika pengguna mengklik "batal" pada alat pilih 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>
Catatan: kode ini tidak mendeteksi pembatalan, ia menawarkan cara untuk menghindari kebutuhan untuk mendeteksinya dalam kasus umum di mana orang mencoba mendeteksinya.
Saya sampai di sini sambil mencari solusi untuk mengunggah file menggunakan input tersembunyi, saya percaya bahwa ini adalah alasan paling umum untuk mencari cara untuk mendeteksi pembatalan input file (buka dialog file -> jika file dipilih kemudian jalankan beberapa kode, jika tidak lakukan apa-apa), inilah solusi saya:
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'));
});
}
Perhatikan bahwa jika tidak ada file yang dipilih maka baris pertama akan kembali hanya sekali selectFile()
dipanggil lagi (atau jika Anda menelepon fileSelectorResolve()
dari tempat lain).
async function logFileName(){
const file = await selectFile();
if(!file) return;
console.log(file.name);
}
Contoh lain:
async function uploadFile(){
const file = await selectFile();
if(!file) return;
// ... make an ajax call here to upload the file ...
}
Anda dapat membuat pemroses perubahan jquery di kolom input dan mendeteksi pengguna tersebut membatalkan atau menutup jendela upload dengan nilai kolom tersebut.
berikut ini contohnya:
//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