Ответы:
Хотя это не прямое решение, а также плохо в том, что оно работает только (насколько я тестировал) с onfocus (требуя довольно ограничивающую блокировку событий), вы можете добиться этого с помощью следующего:
document.body.onfocus = function(){ /*rock it*/ }
Что в этом хорошего, так это то, что вы можете присоединять / отсоединять его вовремя с файловым событием, и он также, кажется, отлично работает со скрытыми входами (определенная привилегия, если вы используете визуальный обходной путь для дерьмового типа ввода по умолчанию = ' файл'). После этого вам просто нужно выяснить, изменилось ли входное значение.
Пример:
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')
}
Посмотреть в действии: http://jsfiddle.net/Shiboe/yuK3r/6/
К сожалению, это работает только в браузерах webkit. Может быть, кто-то еще сможет выяснить решение firefox / IE
addEventListener("focus",fn,{once: true})
. Однако я не мог заставить его вообще выстрелить, используя document.body.addEventListener
... Не знаю почему. Вместо этого я получил тот же результат с window.addEventListener
.
mousemove
событию на window.document
в дополнение к прослушиванию focus
на , window
кажется, работает везде (по крайней мере в современных браузерах, я не забочусь о прошлом десятилетия затонувших судов). По сути, для этой задачи может использоваться любое событие взаимодействия, которое блокируется диалоговым окном открытия файла.
Вы не можете.
Результат диалогового окна файла не отображается в браузере.
change
событие не отправляется.
change
отправляется событие.
Так что я брошу свою шляпу в этот вопрос, так как я придумал новое решение. У меня есть прогрессивное веб-приложение, которое позволяет пользователям снимать фотографии и видео и загружать их. Мы используем WebRTC, когда это возможно, но возвращаемся к средствам выбора файлов HTML5 для устройств с меньшей поддержкой * cough Safari cough *. Если вы работаете специально над мобильным веб-приложением Android / iOS, которое использует встроенную камеру для прямой съемки фотографий / видео, то это лучшее решение, с которым я столкнулся.
Суть этой проблемы в том, что когда страница загружается, то file
есть null
, но затем, когда пользователь открывает диалоговое окно и нажимает «Отмена», file
остается неподвижным null
, следовательно, он не «изменился», поэтому событие «изменение» не запускается. Для настольных компьютеров это не так уж плохо, потому что большинство пользовательских интерфейсов рабочего стола не зависят от знания, когда вызывается отмена, но мобильные пользовательские интерфейсы, которые вызывают камеру для захвата фото / видео, очень зависят от знания, когда нажата отмена.
Первоначально я использовал document.body.onfocus
событие, чтобы определить, когда пользователь вернулся из средства выбора файлов, и это сработало для большинства устройств, но iOS 11.3 сломала его, так как это событие не запускается.
Мое решение - * содрогаться *, чтобы измерить синхронизацию процессора, чтобы определить, находится ли страница в данный момент на переднем плане или в фоновом режиме. На мобильных устройствах время обработки передается приложению, которое в данный момент находится на переднем плане. Когда камера видна, она крадет процессорное время и снижает приоритет браузера. Все, что нам нужно сделать, это измерить, сколько времени на обработку отводится нашей странице, когда при запуске камеры наше доступное время резко упадет. Когда камера закрывается (либо отменяется, либо иным образом), наше доступное время резко увеличивается.
Мы можем измерить время ЦП, используя setTimeout()
для вызова обратного вызова в X миллисекундах, а затем измерить, сколько времени потребовалось для его фактического вызова. Браузер никогда не вызовет его ровно через X миллисекунд, но если он достаточно близок, то мы должны быть на переднем плане. Если браузер находится очень далеко (более чем в 10 раз медленнее, чем запрошено), значит, мы должны работать в фоновом режиме. Базовая реализация этого выглядит так:
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);
});
}
Я протестировал это на последних версиях iOS и Android, вызвав встроенную камеру, установив атрибуты <input />
элемента.
<input type="file" accept="image/*" capture="camera" />
<input type="file" accept="video/*" capture="camcorder" />
На самом деле это сработало намного лучше, чем я ожидал. Он запускает 10 попыток, запрашивая запуск таймера через 25 миллисекунд. Затем он измеряет, сколько времени на самом деле потребовалось для вызова, и если среднее значение 10 попыток составляет менее 50 миллисекунд, мы предполагаем, что мы должны быть на переднем плане, а камера исчезла. Если он превышает 50 миллисекунд, значит, мы все еще должны находиться в фоновом режиме и должны продолжать ждать.
Я использовал, setTimeout()
а не setInterval()
потому, что последний может ставить в очередь несколько вызовов, которые выполняются сразу после друг друга. Это могло резко увеличить шум в наших данных, поэтому я придерживался setTimeout()
этого правила, хотя это немного сложнее.
Эти конкретные числа хорошо работали для меня, хотя я видел хотя бы один случай, когда отключение камеры было обнаружено преждевременно. Я считаю, что это связано с тем, что камера может медленно открываться, и устройство может пройти 10 испытаний, прежде чем оно фактически станет заземленным. Добавление дополнительных испытаний или ожидание 25-50 миллисекунд перед запуском этой функции может быть обходным решением.
К сожалению, это не работает для настольных браузеров. Теоретически возможен тот же трюк, поскольку они отдают приоритет текущей странице над фоновыми страницами. Однако у многих настольных компьютеров достаточно ресурсов, чтобы страница работала на полной скорости даже в фоновом режиме, поэтому на практике эта стратегия не работает.
Одно из альтернативных решений, которые я изучал, было издевательством над файлом FileList
. Мы начинаем с null
in, <input />
а затем, если пользователь открывает камеру и отменяет, он возвращается к нему null
, что не является изменением, и никакое событие не запускается. Одним из решений было бы назначить фиктивный файл для начала <input />
страницы, поэтому установка null
будет изменением, которое вызовет соответствующее событие.
К сожалению, официального способа создать a не существует FileList
, а <input />
элемент требует, FileList
в частности, и не принимает никаких других значений, кроме null
. Естественно, FileList
объекты не могут быть сконструированы напрямую, это связано с некоторыми старыми проблемами безопасности, которые, по-видимому, даже больше не актуальны. Единственный способ получить один за пределами <input />
элемента - использовать хак, который копирует и вставляет данные, чтобы подделать событие буфера обмена, которое может содержать FileList
объект (вы в основном имитируете перетаскивание файла на -ваше-сайт событие). Это возможно в Firefox, но не в iOS Safari, поэтому для моего конкретного случая использования это было нецелесообразно.
Излишне говорить, что это явно смешно. Тот факт, что веб-страницы не получают никаких уведомлений об изменении критического элемента пользовательского интерфейса, просто смехотворен. Это действительно ошибка в спецификации, так как она никогда не предназначалась для пользовательского интерфейса полноэкранного захвата мультимедиа, и отсутствие запуска события «изменения» технически соответствует спецификации.
Однако могут ли производители браузеров признать реальность этого? Эту проблему можно решить либо с помощью нового события «Готово», которое запускается, даже если никаких изменений не происходит, либо вы можете просто запустить «изменение» в любом случае. Да, это было бы против спецификации, но для меня тривиально вывести событие изменения на стороне JavaScript, но принципиально невозможно изобрести собственное событие «done». Даже мое решение на самом деле просто эвристика, если не дает никаких гарантий относительно состояния браузера.
В его нынешнем виде этот API принципиально непригоден для мобильных устройств, и я думаю, что относительно простое изменение браузера могло бы значительно упростить это для веб-разработчиков * сходит с ума *.
Когда вы выбираете файл и нажимаете кнопку «Открыть / отменить», input
элемент должен потерять фокус blur
. Предполагая, что начальное value
значение input
пусто, любое непустое значение в вашем blur
обработчике будет означать ОК, а пустое значение будет означать Отмена.
ОБНОВЛЕНИЕ: blur
не запускается, когда input
скрыт. Поэтому нельзя использовать этот трюк с загрузкой на основе IFRAME, если вы не хотите временно отображать файлы input
.
blur
не запускается при выборе файла. В других браузерах не пробовал.
/* 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
оценивается ли когда-нибудь ваше утверждение?
Большинство из этих решений мне не подходят.
Проблема в том, что никогда не знаешь, какое событие сработает кулаком, это click
или нет change
? Вы не можете предполагать какой-либо порядок, потому что он, вероятно, зависит от реализации браузера.
По крайней мере, в Opera и Chrome (конец 2015 года) click
срабатывает непосредственно перед «заполнением» ввода файлами, поэтому вы никогда не узнаете, сколько времени пройдет files.length != 0
до тех пор, пока вы click
не сработаете change
.
Вот код:
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);
};
});
Просто послушайте также событие щелчка.
Следуя примеру Shiboe, вот пример 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!');
}
});
Вы можете увидеть это в действии здесь: http://jsfiddle.net/T3Vwz
Вы можете поймать отмену, если выберете тот же файл, что и раньше, и нажмете кнопку «Отмена»: в этом случае.
Сделать это можно так:
<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>
Самый простой способ - проверить, есть ли файлы во временной памяти. Если вы хотите получать событие изменения каждый раз, когда пользователь нажимает на ввод файла, вы можете запустить его.
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 было бы хорошим, если бы оно работало на мобильном webkit, но это не так. Что я могу придумать, так это добавить прослушиватель событий mousemove к некоторому объекту dom в то время, когда открывается окно ввода файла, например:
$('.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
};
Событие mousemove блокируется на странице, пока открыто диалоговое окно с файлом, а когда он закрывается, одно проверяет, есть ли какие-либо файлы во входном файле. В моем случае я хочу, чтобы индикатор активности блокировал вещи до тех пор, пока файл не будет загружен, поэтому я хочу удалить свой индикатор только при отмене.
Однако для мобильных устройств это не решает, поскольку нет мыши, которую можно было бы перемещать. Мое решение там далеко не идеально, но я считаю его достаточно хорошим.
$('.upload-progress').bind('touchstart', function() {
checkForFiles(this);
});
Теперь мы прислушиваемся к касанию экрана, чтобы выполнить ту же проверку файлов. Я почти уверен, что после отмены и отключения этого индикатора активности палец пользователя будет довольно быстро помещен на экран.
Можно также просто добавить индикатор активности в событие изменения ввода файла, но на мобильных устройствах часто возникает задержка в несколько секунд между выбором изображения и срабатыванием события изменения, так что это просто намного лучший UX для индикатора активности, который будет отображаться в начало процесса.
Я нашел этот атрибут, пока он самый простой.
if ($('#selectedFile')[0].files.length > 1)
{
// Clicked on 'open' with file
} else {
// Clicked on 'cancel'
}
Вот selectedFile
файл input type=file
.
Я знаю, что это очень старый вопрос, но на случай, если он кому-то поможет, я обнаружил, что при использовании события onmousemove для обнаружения отмены необходимо было проверить два или более таких события за короткий промежуток времени. Это произошло потому, что отдельные события onmousemove генерируются браузером (Chrome 65) каждый раз, когда курсор перемещается из диалогового окна выбора файла и каждый раз, когда он перемещается из главного окна и обратно. Простой счетчик событий движения мыши в сочетании с коротким таймаутом для сброса счетчика до нуля сработало.
В моем случае мне пришлось скрыть кнопку отправки, пока пользователи выбирали изображения.
Вот что я придумываю:
$(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
это мой селектор файлов. Когда кто-то нажимает на нее, я отключаю кнопку отправки формы. Дело в том, что при закрытии диалогового окна файла - неважно, выбирают ли они файл или отменяют - #image-field
фокус возвращается, поэтому я слушаю это событие.
ОБНОВИТЬ
Я обнаружил, что это не работает в safari и poltergeist / phantomjs. Примите во внимание эту информацию, если хотите ее реализовать.
Объединив решения Shiboe и alx, я получил самый надежный код:
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 */
Как правило, событие mousemove помогает, но если пользователь сделал щелчок и затем:
... мы не получим событие mousemove, следовательно, обратного вызова отмены не будет. Более того, если пользователь отменяет второй диалог и перемещает мышь, мы получим 2 обратных вызова отмены. К счастью, специальное событие jQuery focusIn всплывает в документе в обоих случаях, помогая нам избежать таких ситуаций. Единственное ограничение - блокировка события focusIn.
mousemove
событие document
во время выбора файлов в диалоге ОС в Chrome 56.0.2924.87 на Ubuntu 16.10. Нет проблем, если вы не перемещаете мышь или используете только клавиатуру для выбора файла. Мне пришлось заменить mousemove
на, keydown
чтобы обнаружить отмену как можно раньше, но не «слишком рано», когда диалог еще был открыт.
Я вижу, что мой ответ был бы довольно устаревшим, но тем не менее. Я столкнулся с той же проблемой. Итак, вот мое решение. Самый полезный фрагмент кода - это код KGA. Но это не совсем работает и немного сложно. Но я упростил.
Кроме того, основной причиной проблем был тот факт, что событие «изменение» не происходит сразу после фокусировки, поэтому нам нужно подождать некоторое время.
«#appendfile» - на который пользователь нажимает, чтобы добавить новый файл. Hrefs получают события фокуса.
$("#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);
});
Вы можете обнаружить это только в ограниченных обстоятельствах. В частности, в Chrome, если файл был выбран ранее, а затем щелкнули диалоговое окно с файлом и нажали кнопку отмены, Chrome очищает файл и запускает событие onChange.
https://code.google.com/p/chromium/issues/detail?id=2508
В этом сценарии вы можете обнаружить это, обработав событие onChange и проверив свойство files .
Для меня работает следующее (на рабочем столе, в 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;
}
Это действительно использует angularjs $ q, но вы можете заменить его любой другой структурой обещаний, если это необходимо.
Протестировано в IE11, Edge, Chrome, Firefox, но похоже, что он не работает в Chrome на планшете Android, поскольку не запускает событие Focus.
Поле file
-type, к сожалению, не реагирует на множество событий (размытие было бы прекрасно). Я вижу много людей, предлагающих change
ориентированные на решения решения, и за них голосуют против.
change
действительно работает, но у него есть серьезный недостаток (по сравнению с тем, что мы хотим ).
Когда вы только что загружаете страницу, содержащую поле файла, откройте окно и нажмите «Отмена». К сожалению, ничего не меняется .
Я выбрал загрузку в закрытом состоянии.
section#after-image
в моем случае скрыта от просмотра. Когда мои file field
изменения, отображается кнопка загрузки. После успешной загрузки section#after-image
отображается.change
событие запускается этой отменой, и там я могу (и действительно) повторно скрыть свою кнопку загрузки, пока не будет выбран правильный файл.Мне повезло, что это закрытое состояние уже было дизайном моей формы. Вам не нужно использовать один и тот же стиль, просто сначала скрывая кнопку загрузки, а после изменения устанавливайте скрытое поле или переменную javascript на то, что вы можете отслеживать при отправке.
Я попытался изменить значение files [0] до взаимодействия с полем. Это не повлияло на изменение.
Так что да, change
работает, по крайней мере, так хорошо, как мы собираемся получить. Файловое поле защищено по очевидным причинам, но к разочарованию разработчиков с благими намерениями.
Это не соответствует моей цели, но вы могли бы onclick
загрузить предупреждение (не alert()
, потому что это останавливает обработку страницы) и скрыть его, если срабатывает изменение и files [0] имеет значение null. Если изменение не запускается, div остается в своем состоянии.
Есть хакерский способ сделать это (добавить обратные вызовы или разрешить некоторую отложенную / обещающую реализацию вместо alert()
вызовов):
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);
Как это работает: пока открыто диалоговое окно выбора файла, документ не получает событий указателя мыши. Задержка составляет 1000 мс, чтобы диалоговое окно действительно появилось и заблокировало окно браузера. Проверено в Chrome и Firefox (только для Windows).
Но, конечно, это ненадежный способ обнаружить отмененный диалог. Хотя это может улучшить поведение пользовательского интерфейса для вас.
Вот мое решение, использующее фокус ввода файла (без использования таймеров)
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 }
В лучшем случае это хакерство, но вот рабочий пример моего решения, чтобы определить, загрузил ли пользователь файл или нет, и разрешить им продолжить, только если они загрузили файл.
В основном скрыть Continue
, Save
, Proceed
или что - то ваша кнопка. Затем в JavaScript вы берете имя файла. Если имя файла не имеет значения, не показывать Continue
кнопку. Если у него есть значение, покажите кнопку. Это также работает, если они сначала загружают файл, а затем пытаются загрузить другой файл и нажимают «Отмена».
Вот код.
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 во многом это делается для того, чтобы сделать сайт и кнопку доступными для всех. Настройте свою кнопку так, как вам нравится.
Что ж, это не совсем ответ на ваш вопрос. Я предполагаю, что у вас есть сценарий, когда вы добавляете ввод файла и вызываете выбор файла, и если пользователь нажимает кнопку отмены, вы просто удаляете ввод.
Если это так, то: Зачем вводить пустой файл?
Создавайте один на лету, но добавляйте его в DOM только после того, как он заполнен. Примерно так:
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();
Итак, здесь я создаю <input type = "file" /> на лету, привязываюсь к его событию изменения и сразу же вызываю щелчок. При изменении сработает только тогда, когда пользователь выберет файл и нажмет «ОК», в противном случае ввод не будет добавлен в DOM и, следовательно, не будет отправлен.
Рабочий пример здесь: https://jsfiddle.net/69g0Lxno/3/
Если вам уже требуется JQuery, это решение может сработать (это тот самый код, который мне действительно нужен в моем случае, хотя использование обещания просто заставляет код ждать, пока выбор файла не будет решен):
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();
});
В этом потоке есть несколько предлагаемых решений, и эта трудность определения момента, когда пользователь нажимает кнопку «Отмена» в поле выбора файла, является проблемой, которая затрагивает многих людей.
Дело в том, что не существует 100% надежного способа определить, нажал ли пользователь кнопку «Отмена» в поле выбора файла. Но есть способы надежно определить, добавил ли пользователь файл во входной файл. Итак, это основная стратегия этого ответа!
Я решил добавить этот ответ, потому что, по-видимому, другие ответы не работают в большинстве браузеров и не гарантируются на мобильных устройствах.
Вкратце код основан на 3 пунктах:
Для лучшего понимания посмотрите на приведенный ниже код и примечания.
[...]
<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);
}
Спасибо! = D
Мы достигли углового, как показано ниже.
<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();
}
}
Просто добавьте в свой ввод слушателя изменений, тип которого - файл. т.е.
<input type="file" id="file_to_upload" name="file_to_upload" />
Я использовал jQuery, и, очевидно, любой может использовать valina JS (в соответствии с требованиями).
$("#file_to_upload").change(function() {
if (this.files.length) {
alert('file choosen');
} else {
alert('file NOT choosen');
}
});
.change()
вызывается ненадежно, если пользователь нажимает кнопку «Отмена» в средстве выбора файлов.
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>
Примечание: этот код не обнаруживает отмену, он предлагает способ обойти необходимость ее обнаружения в общем случае, когда люди пытаются ее обнаружить.
Я пришел сюда, когда искал решение для загрузки файлов с использованием скрытого ввода, я считаю, что это наиболее распространенная причина искать способ обнаружения отмены ввода файла (диалоговое окно открытия файла -> если файл был выбран, запустите несколько код, иначе ничего не делать), вот мое решение:
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'));
});
}
Обратите внимание, что если файл не был выбран, первая строка вернется только один раз, selectFile()
вызывается снова (или если вы вызывались fileSelectorResolve()
из другого места).
async function logFileName(){
const file = await selectFile();
if(!file) return;
console.log(file.name);
}
Другой пример:
async function uploadFile(){
const file = await selectFile();
if(!file) return;
// ... make an ajax call here to upload the file ...
}
Вы можете создать прослушиватель изменений jquery в поле ввода и определить, что пользователь отменяет или закрывает окно загрузки по значению поля.
вот пример:
//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