Bagaimana cara mendeteksi saat pembatalan diklik pada input file?

112

Bagaimana saya bisa mendeteksi ketika pengguna membatalkan input file menggunakan input file html?

onChange memungkinkan saya mendeteksi kapan mereka memilih file, tetapi saya juga ingin tahu kapan mereka membatalkan (tutup dialog pilih file tanpa memilih apa pun).

Ppp
sumber
Anda juga dapat mendeteksi jika seseorang memilih file dan kemudian mencoba untuk memilih kirim satu tetapi membatalkan, Anda akan kosonge.target.files
jcubic

Jawaban:

45

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

Shiboe
sumber
Saya tidak ingin acara fokus diaktifkan setiap saat, jadi saya ingin menggunakannya 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.
WoodenKitty
3
Ini tidak berfungsi di Firefox dan Edge untuk saya. Namun, mendengarkan untuk mousemoveacara pada window.document selain untuk mendengarkan focuspada windowtampaknya 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.
John Weisz
@WoodenKitty bagaimana Anda menerapkan ini menggunakan addEventListener. Saya mencoba melakukan ini secara bersudut dan document.body juga tidak berfungsi untuk saya
Terrance Jackson
ini tidak berfungsi pada bidang masukan tersembunyi.
Sebastian
20

Tidak boleh.

Hasil dari dialog file tidak diekspos ke browser.

Oded
sumber
Apakah mungkin untuk memeriksa apakah pemilih file terbuka atau tidak?
Ppp
1
@Ppp - Tidak. Browser tidak memberitahumu hal itu.
Oded
6
"Hasil dari dialog file tidak diekspos ke browser." Ini tidak benar jika dialog ditutup dengan memilih file.
Trevor
3
The perubahan dipicu dengan menggunakan dialog file terkena browser. Lihat, jika tidak ada file yang dipilih, dan tidak ada file yang dipilih setelahnya, maka tidak ada yang berubah, sehingga tidak ada changeperistiwa yang dikirim.
John Weisz
1
Selain itu, jika sebuah file telah dipilih, dan Anda memilih file yang sama lagi, tidak ada changeacara yang dikirim untuk itu juga.
Patrick Roberts
16

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, fileis null, tetapi kemudian ketika pengguna membuka dialog dan menekan "Batal", fileitu 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.onfocusacara 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.

Konsep

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.

Penerapan

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.

Beberapa detail tambahan

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.

Desktop

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.

Solusi alternatif

Salah satu solusi alternatif yang tidak banyak disebutkan orang yang saya eksplorasi adalah mengejek a FileList. Kita mulai dengan nulldi <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 FileListkhususnya dan tidak akan menerima nilai lain selain itu null. Secara alami, FileListobjek 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 FileListobjek (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.

Peramban, mohon ...

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

Douglas Parker
sumber
7

Saat Anda memilih file dan mengklik buka / batal, inputelemen akan kehilangan fokus alias blur. Dengan asumsi awal valuedari inputkosong, setiap nilai yang kosong di Anda blurhandler akan mengindikasikan OK, dan nilai kosong berarti Batal.

UPDATE: Tidak blurdipicu saat inputtersembunyi. Jadi tidak dapat menggunakan trik ini dengan unggahan berbasis IFRAME, kecuali Anda ingin menampilkan file input.

zvolkov.dll
sumber
3
Di Firefox 10 beta dan Chrome 16, blurtidak dipicu saat Anda memilih file. Belum mencoba di browser lain.
Blaise
1
Itu tidak berfungsi - blur pada input dipicu segera setelah klik. Chrome 63.0.3239.84 x64 di Win7.
Qwertiy
7
/* Tested on Google Chrome */
$("input[type=file]").bind("change", function() {
    var selected_file_name = $(this).val();
    if ( selected_file_name.length > 0 ) {
        /* Some file selected */
    }
    else {
        /* No file selected or cancel/close
           dialog button clicked */
        /* If user has select a file before,
           when they submit, it will treated as
           no file selected */
    }
});
Rizky
sumber
6
Sudahkah Anda memeriksa apakah elsepernyataan Anda pernah dievaluasi?
jayarjo
Sejauh yang saya ingat ketika saya menulis jawaban ini, jika pengguna memilih file dan kemudian memilih kembali file (tetapi kemudian membatalkan), itu akan diperlakukan sebagai tidak ada file yang dipilih, saya hanya menggunakan peringatan pada variabel selected_file_name untuk mengujinya. Lagi pula, belum mengujinya lagi sejak saat itu.
Rizky
4
Saya dapat mengatakan, setelah beberapa pengujian lintas browser, bahwa Firefox tidak mengaktifkan peristiwa perubahan saat pembatalan, juga tidak menghapus file yang ada yang dipilih jika dibuka kembali dan membatalkan diklik untuk kedua kalinya. Aneh, ya?
mix3d
7

Sebagian besar solusi ini tidak berhasil untuk saya.

Masalahnya adalah Anda tidak pernah tahu peristiwa mana yang akan memicu tinju, apakah itu clickatau apakah itu change? Anda tidak dapat mengasumsikan urutan apa pun, karena mungkin tergantung pada implementasi browser.

Setidaknya di Opera dan Chrome (akhir 2015) clickdipicu tepat sebelum 'mengisi' input dengan file, jadi Anda tidak akan pernah tahu berapa lama files.length != 0sampai Anda menunda clickuntuk 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);
    };
});
superdario128
sumber
Jika Anda mengklik pada halaman ... "Batal diklik" ... = | (Diuji dengan Chrome)
Eduardo Lucio
5

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

AEQ
sumber
1
Saya menguji di Chrome 51, itu tidak berfungsi saat pertama kali Anda memilih file. Solusinya adalah dengan menambahkan penundaan: jsfiddle.net/7jfbmunh
Benoit Blanchon
Biola Anda tidak berhasil untuk saya - menggunakan Firefox 58.0.2
barrypicker
4

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>
loveJs
sumber
1
Thnkx @loveJs posting ini sangat membantu saya.
VPK
4
Tidak persis, jika pengguna memilih file yang sama itu bukan pembatalan = (
Tiago Peres França
13
onchange hanya menyala jika ada perubahan. Jadi jika file tersebut adalah file yang sama seperti sebelumnya, pendengar onchange tidak akan aktif.
Shane
1
Anda dapat menghapus pilihan input (input.value = '') setelah Anda mendeteksi perubahan pertama, sehingga perubahan yang kedua kali akan aktif meskipun pengguna memilih file yang sama lagi.
Chris
1
@CodeGems diizinkan untuk menyetel input.value secara terprogram ke string kosong. Tetapi Anda benar bahwa menyetelnya ke nilai lain tidak diperbolehkan.
Chris
4

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
    }
});
Krzysztof Janiszewski
sumber
2

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.

ransomweaver
sumber
2

Saya menemukan atribut ini, yang paling sederhana.

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

Ini, selectedFileadalah input type=file.

Israel
sumber
Ini adalah solusi yang baik untuk saya, kecuali bahwa seharusnya> = 1
Bron Thulke
2

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.

Steve Brooker
sumber
1
Paragraf pertama adalah jawaban yang bagus, meskipun bisa dipecah menjadi beberapa satu agar lebih mudah dibaca. Tetapi kedua baris "SOAPBOX" itu membuatnya berbatasan dengan Non-Answer karena pertanyaan tidak termasuk dalam jawaban. Bisakah Anda sedikit mengubah / mengubah jawaban Anda? Terima kasih.
Filnor
1

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

bonyiii
sumber
1

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:

  • membatalkan dialog buka file dengan tombol escape (tanpa menggerakkan mouse), melakukan klik akurat lagi untuk membuka dialog file lagi ...
  • mengalihkan fokus ke aplikasi lain, lalu kembali ke dialog buka file browser dan menutupnya, lalu dibuka lagi melalui tombol enter atau spasi ...

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

KGA
sumber
Saya menangkap mousemoveacara tersebut documentsaat 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 mousemoveby keydownuntuk mendeteksi pembatalan sedini mungkin, tetapi tidak "terlalu dini", saat dialog masih terbuka.
Ferdinand Prantl
1

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

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 .

Kunal
sumber
1

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.

Peter
sumber
1

Bidang file-type, dengan frustrasi, tidak merespons banyak peristiwa (blur akan menyenangkan). Saya melihat banyak orang menyarankanchange solusi berorientasi dan mereka mendapatkan suara negatif.

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

  • Bagian selanjutnya dari formulir a section#after-imagedalam kasus saya tersembunyi dari pandangan. Saat saya file fieldberubah, tombol unggah ditampilkan. Setelah unggahan berhasil, section#after-imageditampilkan.
  • Jika pengguna memuat, membuka file-dialog, kemudian membatalkan, mereka tidak pernah melihat tombol unggah.
  • Jika pengguna memilih file, tombol unggah akan ditampilkan. Jika mereka kemudian membuka dialog dan membatalkan, changeperistiwa 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, changeberhasil, 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.

Joe biasa
sumber
0

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.

alx
sumber
Salah satu kelemahan utama ada pada ponsel dan tablet, mereka tidak menggunakan mouse secara normal!
Peter
0

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

Yovav
sumber
Menjalankan cuplikan Anda memberi sayaError: { "message": "Uncaught SyntaxError: missing ) after argument list", "filename": "https://stacksnippets.net/js", "lineno": 51, "colno": 1 }
connexo
0

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, Proceedatau apa pun tombol Anda. Kemudian di JavaScript Anda mengambil nama file. Jika nama file tidak memiliki nilai, maka jangan tampilkan Continuetombolnya. 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.

maxshuty
sumber
0

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/

Alexey
sumber
0

// Gunakan hover, bukan blur

var fileInput = $("#fileInput");

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

    //open
} else {

}
jsil
sumber
0

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

});

pengguna2397355
sumber
0

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:

  1. File input awalnya dibuat secara dinamis di "memory" di js (kami tidak menambahkannya ke "HTML" saat ini);
  2. Setelah menambahkan file kemudian file input ditambahkan ke HTML, jika tidak, tidak ada yang terjadi;
  3. Penghapusan file dilakukan dengan menghapus file input dari HTML oleh peristiwa tertentu, yang berarti bahwa "pengeditan" / "modifikasi" file dilakukan dengan menghapus file input lama dan membuat yang baru.

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

Eduardo Lucio
sumber
0

Kami mencapai sudut seperti di bawah ini.

    1. mengikat acara klik pada file jenis masukan.
      1. Lampirkan acara fokus dengan jendela dan tambahkan kondisi jika uploadPanel benar, lalu tampilkan konsol.
      2. ketika klik pada file jenis masukan, nilai uploadPanel boolean adalah true. dan kotak dialog muncul.
      3. ketika membatalkan ATAU tombol Esc klik kemudian kotak dialog apotik dan konsol muncul.

HTML

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

TS

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



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

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

            }

    });
Naveed Ali
sumber
.change()tidak dapat dipanggil secara andal jika pengguna mengklik "batal" pada alat pilih file.
Ben Wheeler
@benWheeler, saya telah memeriksanya di chrome dan firefox dan berfungsi dengan baik tetapi belum diuji di browser lain.
Naveed Ali
0
    enter code here
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <h1>Hello</h1>
    <div id="cancel01">
      <button>Cancel</button>
    </div>

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

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

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

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

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

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

          /******
           *The below code will delete the file path
           ******/
          nameInput.value = '';
        }
      });
    </script>
  </body>
</html>
Emmanuel Onah
sumber
Jawaban hanya kode dianggap berkualitas rendah: pastikan untuk memberikan penjelasan tentang fungsi kode Anda dan bagaimana cara menyelesaikan masalah. Ini akan membantu penanya dan pembaca yang akan datang jika Anda dapat menambahkan lebih banyak informasi dalam posting Anda. Lihat Menjelaskan sepenuhnya jawaban berbasis kode
Calos
0

Solusi untuk pemilihan file dengan input tersembunyi

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

Contoh penggunaan:

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 ...
}
kentang
sumber
Di chrome 83.0, saya tidak mendapatkan panggilan balik untuk peristiwa masukan saat pengguna membatalkan, dan tidak pada percobaan kedua
Soylent Graham
1
@SoylentGraham Ya, karena kode ini tidak mendeteksi pembatalan, ia menawarkan cara untuk menghindari kebutuhan untuk mendeteksinya dalam kasus umum di mana orang mencoba mendeteksinya. Karena saya tidak jelas tentang itu, saya menambahkan komentar ini ke jawaban saya.
kentang
Saya salah, saya pikir, saya salah membaca jawaban Anda sebagai "pengguna membatalkan percobaan kedua"
Soylent Graham
-1

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
    }
});
Mohamed Hana
sumber
Tidak tahu mengapa ini tidak disukai - inilah yang saya cari. Terima kasih! 🙂
pengguna1274820