Yanıtlar:
Doğrudan bir çözüm olmasa da, yalnızca (test ettiğim kadarıyla) onfocus ile çalıştığı için kötü olsa da (oldukça sınırlayıcı bir olay engelleme gerektirir) bunu aşağıdakilerle başarabilirsiniz:
document.body.onfocus = function(){ /*rock it*/ }
Bunun güzel yanı, onu dosya olayıyla zamanında ekleyebilmeniz / ayırabilmeniz ve ayrıca gizli girişlerle iyi çalışıyor gibi görünmesi (berbat varsayılan giriş türü için görsel bir geçici çözüm kullanıyorsanız kesin bir avantaj = ') dosya'). Bundan sonra, giriş değerinin değişip değişmediğini bulmanız yeterlidir.
Bir örnek:
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')
}
İş başında görün: http://jsfiddle.net/Shiboe/yuK3r/6/
Ne yazık ki, yalnızca webkit tarayıcılarında çalışıyor gibi görünüyor. Belki başka biri Firefox / IE çözümünü bulabilir
addEventListener("focus",fn,{once: true})
. Ancak onu kullanarak ateşlemeyi hiç document.body.addEventListener
başaramadım .. Nedenini bilmiyorum. Bunun yerine ile aynı sonucu aldım window.addEventListener
.
mousemove
üzerine olay window.document
ilaveten dinlemeye focus
üzerine window
(modern tarayıcılarda en azından ben son on yıl batıklar umurumda değil) her yerde çalışıyor gibi görünüyor. Temel olarak, bu görev için açık bir dosya açma iletişim kutusu tarafından engellenen herhangi bir etkileşim olayı kullanılabilir.
Yapamazsın.
Dosya iletişim kutusunun sonucu tarayıcı tarafından gösterilmez.
change
olay gönderilmemiştir.
change
olay gönderilmez.
Bu yüzden, yeni bir çözüm bulduğum için bu soruya şapkamı atacağım. Kullanıcıların fotoğraf ve video çekmesine ve bunları yüklemesine olanak tanıyan Progressive Web Uygulamam var. Mümkün olduğunda WebRTC kullanıyoruz, ancak daha az destek * öksürük Safari öksürüğü * olan cihazlar için HTML5 dosya seçicilere geri dönüyoruz. Doğrudan fotoğraf / video çekmek için yerel kamerayı kullanan bir Android / iOS mobil web uygulamasında özellikle çalışıyorsanız, o zaman karşılaştığım en iyi çözüm budur.
Bu sorunun özü, sayfa yüklendiğinde, file
olur null
, ancak daha sonra kullanıcı iletişim kutusunu açıp "İptal" e bastığında file
, hala null
"değişmez", dolayısıyla "değişiklik" olayının tetiklenmemesidir. Masaüstü bilgisayarlar için bu çok da kötü değil çünkü çoğu masaüstü kullanıcı arayüzü bir iptalin ne zaman çağrıldığını bilmeye bağlı değil, ancak kamerayı bir fotoğraf / video çekmek için getiren mobil kullanıcı arayüzleri , iptalin ne zaman basıldığını bilmeye çok bağlı.
Aslında document.body.onfocus
olayı, kullanıcının dosya seçiciden ne zaman döndüğünü algılamak için kullandım ve bu çoğu cihaz için işe yaradı, ancak iOS 11.3, bu olay tetiklenmediğinden bunu bozdu.
Buna çözümüm, sayfanın o anda ön planda mı yoksa arka planda mı olduğunu belirlemek için CPU zamanlamasını ölçmek için * titreme *. Mobil cihazlarda, şu anda ön plandaki uygulamaya işlem süresi verilir. Bir kamera göründüğünde, CPU zamanını çalar ve tarayıcının önceliğini kaldırır. Tek yapmamız gereken, sayfamıza ne kadar işlem süresi verildiğini ölçmek, kamera başladığında kullanılabilir süremiz büyük ölçüde düşecek. Kamera kapatıldığında (iptal edildiğinde veya başka şekilde), mevcut zamanımız tekrar yükselir.
setTimeout()
X milisaniye cinsinden bir geri aramayı çağırmak için kullanarak CPU zamanlamasını ölçebilir ve ardından gerçekten çağırmanın ne kadar sürdüğünü ölçebiliriz. Tarayıcı asla tam olarak X milisaniyeden sonra onu çağırmaz, ancak makul bir yakınlık söz konusuysa, o zaman ön planda olmalıyız. Tarayıcı çok uzaktaysa (istenenden 10 kat daha yavaş), o zaman arka planda olmalıyız. Bunun temel bir uygulaması şu şekildedir:
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);
});
}
Bunu iOS ve Android'in son sürümlerinde test ettim ve öğedeki öznitelikleri ayarlayarak yerel kamerayı açtım <input />
.
<input type="file" accept="image/*" capture="camera" />
<input type="file" accept="video/*" capture="camcorder" />
Bu aslında beklediğimden çok daha iyi sonuç veriyor. Bir zamanlayıcının 25 milisaniyede çağrılmasını talep ederek 10 deneme çalıştırır. Ardından, çağırmanın gerçekte ne kadar sürdüğünü ölçer ve 10 denemenin ortalaması 50 milisaniyeden azsa, ön planda olmamız gerektiğini ve kameranın gittiğini varsayarız. 50 milisaniyeden fazlaysa, o zaman hala arka planda olmalı ve beklemeye devam etmeliyiz.
Bunun setTimeout()
yerine setInterval()
, ikincisi, birbirinin hemen ardından çalışan birden çok çağrıyı sıraya koyabildiği için kullandım. Bu, verilerimizdeki gürültüyü büyük ölçüde artırabilir, bu yüzden bunu setTimeout()
yapmak biraz daha karmaşık olsa da buna bağlı kaldım.
Bu belirli sayılar benim için iyi çalıştı, ancak en az bir kez kameranın kapatılmasının erken tespit edildiği bir örnek gördüm. Bunun, kameranın açılmasının yavaş olabileceğine ve cihazın arka plana alınmadan önce 10 deneme çalıştırabileceğine inanıyorum. Bu işlevi başlatmadan önce daha fazla deneme eklemek veya 25-50 milisaniye beklemek bunun için bir çözüm olabilir.
Ne yazık ki, bu gerçekten masaüstü tarayıcılarda işe yaramıyor. Teorik olarak, aynı numara, mevcut sayfayı arka planlı sayfalara göre önceliklendirdiklerinden mümkündür. Ancak birçok masaüstü bilgisayarın arka planda olsa bile sayfanın tam hızda çalışmasını sağlayacak kadar kaynağı vardır, bu nedenle bu strateji pratikte gerçekten işe yaramaz.
Bir alternatif çözüm, keşfettiğimden pek fazla insan bahsetmediğinden, a FileList
. Biz başlamak null
içinde <input />
kullanıcı kamerayı açar ve onlar geri gelmek iptal ederse ve daha sonra null
bir değişiklik olmadığı ve hiçbir olay tetikleyecek olan. Bir çözüm <input />
, sayfanın başlangıcına sahte bir dosya atamak olabilir , bu nedenle null
, uygun olayı tetikleyecek bir değişiklik olacaktır.
Ne yazık ki, a oluşturmanın resmi bir yolu yoktur FileList
ve <input />
öğe FileList
özellikle a gerektirir ve bunun dışında başka bir değeri kabul etmez null
. Doğal olarak, FileList
nesneler doğrudan inşa edilemez, görünüşe göre artık alakalı olmayan bazı eski güvenlik sorunları için geçerlidir. Bir <input />
öğenin dışından birini elde etmenin tek yolu , bir FileList
nesne içerebilen bir pano olayını taklit etmek için verileri kopyalayıp yapıştıran bir hack kullanmaktır (temelde bir dosyaya sürükle ve bırak numarası yapıyorsunuz. - web sitesi etkinliğiniz). Bu Firefox'ta mümkündür, ancak iOS Safari için geçerli değildir, bu nedenle benim özel kullanım durumum için uygun değildi.
Bunun açıkça saçma olduğunu söylemeye gerek yok. Web sayfalarına kritik bir UI öğesinin değiştiğine dair sıfır bildirim verilmesi gerçeği gülünç. Bu, hiçbir zaman tam ekran bir medya yakalama kullanıcı arabirimi için tasarlanmadığı ve teknik olarak "değişiklik" olayını tetiklemediği için spesifikasyondaki bir hatadır .
Ancak , tarayıcı satıcıları bunun gerçekliğini anlayabilir mi? Bu, herhangi bir değişiklik olmadığında bile tetiklenen yeni bir "tamamlandı" olayıyla çözülebilir veya yalnızca "değişikliği" tetikleyebilirsiniz. Evet, bu spesifikasyona aykırı olur, ancak JavaScript tarafında bir değişiklik olayını tekilleştirmek benim için önemsiz, ancak kendi "tamamlandı" olayımı icat etmem temelde imkansız. Tarayıcının durumu hakkında hiçbir garanti sunmuyorsa benim çözümüm bile gerçekten sadece sezgiseldir.
Hali hazırda, bu API temelde mobil cihazlar için kullanılamaz ve bence nispeten basit bir tarayıcı değişikliğinin bunu web geliştiricileri için son derece kolay hale getirebileceğini * sabun kutusundan çıkarlar *.
Bir dosya seçip aç / iptal et'i tıkladığınızda, input
öğe odağı yani odağı kaybetmelidir blur
. İlk varsayarsak value
ait input
boştur, ta istediğiniz olmayan boş değer blur
işleyicisi bir Tamam işaret eder ve boş bir değer, bir iptal anlamına gelecektir.
GÜNCELLEME: gizlendiğinde blur
tetiklenmez input
. Dolayısıyla, geçici olarak .txt dosyasını görüntülemek istemediğiniz sürece, bu numarayı IFRAME tabanlı yüklemelerde kullanamazsınız input
.
blur
bir dosya seçtiğinizde tetiklenmez. Diğer tarayıcılarda denemedim.
/* 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
İfadenizin değerlendirilip değerlendirilmediğini kontrol ettiniz mi?
Bu çözümlerin çoğu benim için çalışmıyor.
Sorun şu ki, hangi olayın ilk tetikleneceğini asla bilemezsiniz click
, öyle change
mi yoksa değil mi? Muhtemelen tarayıcının uygulamasına bağlı olduğu için herhangi bir sıra üstlenemezsiniz.
En azından Opera ve Chrome'da (2015'in sonlarında) click
, girdileri dosyalarla 'doldurmadan' hemen önce tetiklenir, bu nedenle, daha sonra tetiklenmeyi files.length != 0
geciktirene kadar ne kadar süreyi asla bilemezsiniz .click
change
İşte kod:
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);
};
});
Sadece tıklama olayını da dinleyin.
Shiboe'nin örneğini takiben, işte bir jQuery örneği:
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!');
}
});
Burada eylem halinde görebilirsiniz: http://jsfiddle.net/T3Vwz
Öncekiyle aynı dosyayı seçerseniz ve bu durumda iptal'i tıklatırsanız iptali yakalayabilirsiniz.
Bunu şu şekilde yapabilirsiniz:
<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>
En kolay yol, geçici bellekte herhangi bir dosya olup olmadığını kontrol etmektir. Kullanıcı dosya girişini her tıkladığında değişiklik olayını almak istiyorsanız, onu tetikleyebilirsiniz.
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
}
});
Shiboe'nun çözümü, mobil webkit üzerinde çalışsaydı iyi olurdu, ama olmadı. Bulabileceğim şey, dosya giriş penceresi açıldığında bazı dom nesnelerine bir mousemove olay dinleyicisi eklemektir, örneğin:
$('.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
};
Dosya iletişim kutusu açıkken mousemove olayı sayfadan engellenir ve kapatıldığında dosya girişinde herhangi bir dosya olup olmadığını kontrol eder. Benim durumumda, dosya yüklenene kadar işleri engelleyen bir etkinlik göstergesi istiyorum, bu nedenle yalnızca iptal durumunda göstergemi kaldırmak istiyorum.
Ancak hareket edecek fare olmadığından bu durum mobil cihazlar için çözülmez. Benim çözümüm mükemmelden daha az ama bence yeterince iyi.
$('.upload-progress').bind('touchstart', function() {
checkForFiles(this);
});
Şimdi aynı dosyaları kontrol etmek için ekranda bir dokunuş dinliyoruz. Bu aktivite göstergesini iptal edip kapattıktan sonra kullanıcının parmağının oldukça hızlı bir şekilde ekrana konacağından oldukça eminim.
Etkinlik göstergesi dosya girdisi değişiklik olayına da eklenebilir, ancak mobil cihazlarda görüntüyü seçme ve değişiklik etkinliğinin tetiklenmesi arasında genellikle birkaç saniyelik bir gecikme olur, bu nedenle etkinlik göstergesinin görüntülenmesi için çok daha iyi UX sürecin başlangıcı.
Bu atribute'u buldum, şimdiye kadarki en basit.
if ($('#selectedFile')[0].files.length > 1)
{
// Clicked on 'open' with file
} else {
// Clicked on 'cancel'
}
İşte selectedFile
bir input type=file
.
Bunun çok eski bir soru olduğunu biliyorum, ancak birisine yardımcı olması durumunda, iptali tespit etmek için onmousemove olayını kullanırken, kısa bir süre içinde bu tür iki veya daha fazla olayı test etmenin gerekli olduğunu anladım. Bunun nedeni, imleç dosya seçme iletişim penceresinden her dışarı hareket ettirildiğinde ve ana pencereden her dışarı taşındığında ve her defasında tarayıcı (Chrome 65) tarafından tek onmousemove olaylarının oluşturulmasıydı. Fare hareketi olaylarının basit bir sayacı sayacı sıfırlamak için kısa süreli bir zaman aşımı ile birleştiğinde bir tedavi çalıştı.
Benim durumumda, kullanıcılar resimleri seçerken gönder düğmesini gizlemek zorunda kaldım.
Bu benim çıktığım şey:
$(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
benim dosya seçicim. Bir kişi üzerine tıkladığında, form gönderme butonunu devre dışı bırakıyorum. Mesele şu ki, dosya diyaloğu kapatıldığında - bir dosya seçmeleri veya iptal etmeleri önemli değil - #image-field
odağı geri aldım, bu yüzden o olayı dinliyorum.
GÜNCELLEME
Bunun safari ve poltergeist / phantomjs'de çalışmadığını buldum. Uygulamak istiyorsanız bu bilgileri dikkate alın.
Shiboe ve alx'in çözümlerini birleştiren en güvenilir koda sahibim:
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 */
Genellikle, mousemove olayı hile yapar, ancak kullanıcının bir tıklama yapması ve ardından:
... mousemove olayını almayacağız, dolayısıyla geri aramayı iptal etmeyeceğiz. Ayrıca, kullanıcı ikinci iletişim kutusunu iptal ederse ve bir fareyi hareket ettirirse, 2 iptal geri çağrısı alırız. Neyse ki, özel jQuery focusIn olayı, her iki durumda da belgeye kabarcıklar vererek bu tür durumlardan kaçınmamıza yardımcı olur. Tek sınırlama, birisinin focusIn olayını da engellemesidir.
mousemove
olayı yakaladım document
. Fareyi hareket ettirmediğinizde veya dosyayı seçmek için sadece klavye kullandığınızda sorun yok. Ben değiştirmek zorunda mousemove
tarafından keydown
mümkün olduğu kadar erken iptal tespit etmek, ancak "çok erken" iletişim hala açık iken.
Cevabımın oldukça modası geçmiş olacağını görüyorum, ama asla daha az değil. Ben de aynı problemle karşılaştım. İşte benim çözümüm. Alınan en kullanışlı kod KGA'nın koduydu. Ancak tamamen çalışmıyor ve biraz karmaşık. Ama basitleştirdim.
Ayrıca, asıl sorun yaratan şey, bu 'değişim' olayının odaklandıktan hemen sonra gelmemesi, bu yüzden bir süre beklememiz gerektiğiydi.
"#appendfile" - kullanıcının yeni bir dosya eklemek için tıkladığı dosya. Href'ler odak olaylarını alır.
$("#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);
});
Bunu yalnızca sınırlı durumlarda tespit edebilirsiniz. Özellikle, kromda daha önce bir dosya seçilmişse ve ardından dosya iletişim kutusu tıklanıp iptal tıklandığında, Chrome dosyayı temizler ve onChange olayını çalıştırır.
https://code.google.com/p/chromium/issues/detail?id=2508
Bu senaryoda, onChange olayını işleyerek ve files özelliğini kontrol ederek bunu saptayabilirsiniz .
Aşağıdakiler benim için çalışıyor gibi görünüyor (masaüstünde, pencerelerde):
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;
}
Bu, angularjs $ q kullanır, ancak gerekirse başka bir vaat çerçevesiyle değiştirebilmelisiniz.
IE11, Edge, Chrome, Firefox'ta test edildi, ancak Focus olayını tetiklemediği için bir Android Tablet üzerindeki Chrome'da çalışmıyor gibi görünüyor.
file
Tipi alan bombalanmasını, olayların bir sürü (bulanıklık güzel olurdu) yanıt vermez. change
Pek çok insanın odaklı çözümler önerdiğini ve onların oylarının reddedildiğini görüyorum.
change
işe yarıyor, ancak büyük bir kusuru var (ne olmasını istediğimize karşı).
Dosya alanı içeren bir sayfayı yeni yüklediğinizde, kutuyu açın ve iptal düğmesine basın. Hiçbir şey sinir bozucu bir şekilde değişmez .
Yapmayı seçtiğim şey kapılı bir durumda yüklemek.
section#after-image
Benim durumumda a formunun bir sonraki kısmı görünmez. Ne zaman file field
değişiklikler, bir yükleme düğmesi gösterilir. Başarılı yüklemenin ardından section#after-image
gösterilir.change
olay bu iptal ile tetiklenir ve burada uygun bir dosya seçilene kadar yükleme düğmemi yeniden gizleyebilirim (ve yapabilirim).Şanslıydım ki bu geçitli durum zaten formumun tasarımıydı. Aynı stili kullanmanıza gerek yoktur, yalnızca yükleme düğmesi başlangıçta gizlenir ve değiştirildiğinde, gönderirken izleyebileceğiniz bir şeye gizli bir alan veya javascript değişkeni ayarlar.
Alanla etkileşim kurulmadan önce [0] dosyaların değerini değiştirmeyi denedim. Bu, değişimle ilgili hiçbir şey yapmadı.
Yani evet, change
en azından alacağımız kadar iyi çalışıyor. Dosya alanı, bariz nedenlerle, ancak iyi niyetli geliştiricilerin hayal kırıklığına uğrayarak güvenceye alınmıştır.
Amacıma uymuyor, ancak onclick
bir uyarı istemi yükleyebilir ( alert()
sayfa işlemeyi durdurduğu için değil ) ve değişiklik tetiklenirse ve dosyalar [0] boşsa gizleyebilirsiniz. Değişiklik tetiklenmezse, div durumunda kalır.
Bunu yapmanın hileli bir yolu vardır (geri aramalar ekleyin veya alert()
aramalar yerine bazı ertelenmiş / vaat edilen uygulamaları çözün ):
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);
Nasıl çalışır: dosya seçimi iletişim kutusu açıkken, belge fare işaretçisi olaylarını almaz. İletişim kutusunun gerçekten görünmesine ve tarayıcı penceresini engellemesine izin vermek için 1000 ms gecikme vardır. Chrome ve Firefox'ta kontrol edildi (yalnızca Windows).
Ancak elbette bu, iptal edilen diyaloğu tespit etmenin güvenilir bir yolu değildir. Yine de, sizin için bazı UI davranışlarını iyileştirebilir.
İşte benim çözümüm, dosya girişi odağını kullanarak (herhangi bir zamanlayıcı kullanmadan)
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 }
Bu en iyi ihtimalle bilgisayar korsanlığıdır, ancak işte bir kullanıcının bir dosya yükleyip yüklemediğini tespit etmek ve yalnızca bir dosya yüklediyse devam etmelerine izin vermek için benim çözümümün çalışan bir örneğini burada bulabilirsiniz .
Temelde gizlemek Continue
, Save
, Proceed
veya ne olursa olsun düğme. Ardından JavaScript'te dosya adını alırsınız. Dosya adının bir değeri yoksa, Continue
düğmeyi göstermeyin . Bir değeri varsa, düğmeyi gösterin. Bu, ilk başta bir dosya yüklediklerinde ve ardından farklı bir dosya yüklemeye çalıştıklarında ve iptal düğmesine tıkladıklarında da işe yarar.
İşte kod.
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;
}
CSS için bunun çoğu, web sitesini ve düğmeyi herkes için erişilebilir kılmak içindir. Düğmenizi istediğiniz gibi şekillendirin.
Bu tam olarak sorunuzu cevaplamıyor. Benim varsayımıma göre, bir senaryonuz var, bir dosya girişi eklediğinizde ve dosya seçimini başlattığınızda ve kullanıcı iptal ederse, girdiyi kaldırırsınız.
Durum böyleyse: Neden boş dosya girişi ekleyelim?
Birini anında oluşturun, ancak yalnızca doldurulduğunda DOM'a ekleyin. Şöyle:
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();
Bu yüzden burada anında <input type = "file" /> oluşturuyorum, değişim olayına bağlanıyorum ve hemen tıklamayı çağırıyorum. Değişiklik durumunda, yalnızca kullanıcı bir dosya seçip Tamam'a bastığında tetiklenecektir, aksi takdirde girdi DOM'a eklenmeyecek ve bu nedenle gönderilmeyecektir.
Burada çalışma örneği: https://jsfiddle.net/69g0Lxno/3/
Zaten JQuery'ye ihtiyacınız varsa, bu çözüm işi yapabilir (bu, benim durumumda gerçekten ihtiyaç duyduğum kodun aynısıdır, ancak bir Söz kullanmak, kodu dosya seçimi çözülene kadar beklemeye zorlamaktır):
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();
});
Bu iş parçacığında önerilen birkaç çözüm vardır ve kullanıcının dosya seçim kutusundaki "İptal" düğmesini tıkladığını tespit etmenin bu zorluğu birçok kişiyi etkileyen bir sorundur.
Gerçek şu ki , kullanıcının dosya seçim kutusundaki "İptal" düğmesine tıklayıp tıklamadığını tespit etmenin% 100 güvenilir bir yolu yoktur . Ancak kullanıcının girdi dosyasına bir dosya ekleyip eklemediğini güvenilir bir şekilde tespit etmenin yolları vardır . Bu, bu cevabın temel stratejisidir!
Bu yanıtı eklemeye karar verdim çünkü görünüşe göre diğer yanıtlar çoğu tarayıcıda çalışmıyor veya mobil cihazlarda garanti edilmiyor.
Kısaca kod 3 noktaya dayanmaktadır:
Daha iyi anlamak için aşağıdaki koda ve notlara da bakın.
[...]
<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);
}
Teşekkürler! = D
Aşağıdaki gibi açısal olarak başardık.
<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();
}
}
Sadece türü dosya olan girdinize 'değişiklik' dinleyicisi ekleyin. yani
<input type="file" id="file_to_upload" name="file_to_upload" />
JQuery kullanarak yaptım ve tabii ki herkes valina JS'yi kullanabilir (gereksinime göre).
$("#file_to_upload").change(function() {
if (this.files.length) {
alert('file choosen');
} else {
alert('file NOT choosen');
}
});
.change()
kullanıcı dosya seçicide "iptal" i tıklarsa güvenilir şekilde çağrılmaz.
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>
Not: Bu kod iptali algılamaz, insanların onu tespit etmeye çalıştıkları yaygın bir durumda onu tespit etme ihtiyacını aşmanın bir yolunu sunar.
Buraya gizli bir girdi kullanarak dosya yüklemeleri için bir çözüm ararken geldim, bunun dosya girdisinin iptalini tespit etmenin en yaygın nedeni olduğuna inanıyorum (dosya iletişim kutusunu aç -> bir dosya seçildiyse, bazılarını çalıştırın kod, aksi takdirde hiçbir şey yapmayın), işte benim çözümüm:
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'));
});
}
Hiçbir dosya seçilmediyse, ilk satırın yalnızca bir kez döneceğini unutmayın selectFile()
(veya fileSelectorResolve()
başka bir yerden ararsanız).
async function logFileName(){
const file = await selectFile();
if(!file) return;
console.log(file.name);
}
Başka bir örnek:
async function uploadFile(){
const file = await selectFile();
if(!file) return;
// ... make an ajax call here to upload the file ...
}
Girdi alanında bir jquery değişiklik dinleyicisi oluşturabilir ve alanın değerine göre kullanıcının iptal ettiği veya kapattığı yükleme penceresini tespit edebilirsiniz.
işte bir örnek:
//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