Как определить, когда при вводе файла нажимается отмена?


112

Как я могу определить, когда пользователь отменяет ввод файла, используя ввод файла html?

onChange позволяет мне определять, когда они выбирают файл, но я также хотел бы знать, когда они отменяют (закройте диалоговое окно выбора файла, ничего не выбирая).


Вы также можете определить, если кто-то выбрал файл, а затем попытался отправить его, но отменил, вы получите пустойe.target.files
jcubic

Ответы:


45

Хотя это не прямое решение, а также плохо в том, что оно работает только (насколько я тестировал) с 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


Я не хотел, чтобы событие focus запускалось каждый раз, поэтому я хотел использовать addEventListener("focus",fn,{once: true}). Однако я не мог заставить его вообще выстрелить, используя document.body.addEventListener... Не знаю почему. Вместо этого я получил тот же результат с window.addEventListener.
WoodenKitty

3
У меня это не работает в Firefox и Edge. Однако, прислушиваясь к mousemoveсобытию на window.document в дополнение к прослушиванию focusна , windowкажется, работает везде (по крайней мере в современных браузерах, я не забочусь о прошлом десятилетия затонувших судов). По сути, для этой задачи может использоваться любое событие взаимодействия, которое блокируется диалоговым окном открытия файла.
Джон Вайс

@W WoodenKitty, как вы реализовали это с помощью addEventListener. Я пытаюсь сделать это в angular, и document.body у меня тоже не работает
Терренс Джексон

это не работает со скрытыми полями ввода.
Себастьян

20

Вы не можете.

Результат диалогового окна файла не отображается в браузере.


Можно ли проверить, открыт ли селектор файлов?
Ppp

1
@Ppp - Нет. Браузер вам этого не сообщает.
Oded

6
«Результат диалогового окна файла не отображается в браузере». Это неверно, если диалоговое окно закрывается путем выбора файла.
Trevor

3
Изменение вызвано с помощью диалогового окна файла подвергается браузер. Видите ли, если не выбран ни один файл, а затем не выбран ни один файл, значит, ничего не изменилось, поэтому changeсобытие не отправляется.
Джон Вайс

1
Кроме того, если файл уже выбран, и вы снова выбираете тот же файл, для этого также не changeотправляется событие.
Патрик Робертс

16

Так что я брошу свою шляпу в этот вопрос, так как я придумал новое решение. У меня есть прогрессивное веб-приложение, которое позволяет пользователям снимать фотографии и видео и загружать их. Мы используем 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. Мы начинаем с nullin, <input />а затем, если пользователь открывает камеру и отменяет, он возвращается к нему null, что не является изменением, и никакое событие не запускается. Одним из решений было бы назначить фиктивный файл для начала <input />страницы, поэтому установка nullбудет изменением, которое вызовет соответствующее событие.

К сожалению, официального способа создать a не существует FileList, а <input />элемент требует, FileListв частности, и не принимает никаких других значений, кроме null. Естественно, FileListобъекты не могут быть сконструированы напрямую, это связано с некоторыми старыми проблемами безопасности, которые, по-видимому, даже больше не актуальны. Единственный способ получить один за пределами <input />элемента - использовать хак, который копирует и вставляет данные, чтобы подделать событие буфера обмена, которое может содержать FileListобъект (вы в основном имитируете перетаскивание файла на -ваше-сайт событие). Это возможно в Firefox, но не в iOS Safari, поэтому для моего конкретного случая использования это было нецелесообразно.

Браузеры, пожалуйста ...

Излишне говорить, что это явно смешно. Тот факт, что веб-страницы не получают никаких уведомлений об изменении критического элемента пользовательского интерфейса, просто смехотворен. Это действительно ошибка в спецификации, так как она никогда не предназначалась для пользовательского интерфейса полноэкранного захвата мультимедиа, и отсутствие запуска события «изменения» технически соответствует спецификации.

Однако могут ли производители браузеров признать реальность этого? Эту проблему можно решить либо с помощью нового события «Готово», которое запускается, даже если никаких изменений не происходит, либо вы можете просто запустить «изменение» в любом случае. Да, это было бы против спецификации, но для меня тривиально вывести событие изменения на стороне JavaScript, но принципиально невозможно изобрести собственное событие «done». Даже мое решение на самом деле просто эвристика, если не дает никаких гарантий относительно состояния браузера.

В его нынешнем виде этот API принципиально непригоден для мобильных устройств, и я думаю, что относительно простое изменение браузера могло бы значительно упростить это для веб-разработчиков * сходит с ума *.



7

Когда вы выбираете файл и нажимаете кнопку «Открыть / отменить», inputэлемент должен потерять фокус blur. Предполагая, что начальное valueзначение inputпусто, любое непустое значение в вашем blurобработчике будет означать ОК, а пустое значение будет означать Отмена.

ОБНОВЛЕНИЕ: blurне запускается, когда inputскрыт. Поэтому нельзя использовать этот трюк с загрузкой на основе IFRAME, если вы не хотите временно отображать файлы input.


3
В Firefox 10 beta и Chrome 16 blurне запускается при выборе файла. В других браузерах не пробовал.
Blaise

1
Это не работает - размытие при вводе срабатывает сразу после щелчка. Chrome 63.0.3239.84 x64 на 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 */
    }
});

6
Вы проверяли, elseоценивается ли когда-нибудь ваше утверждение?
jayarjo

Насколько я помню, когда я писал этот ответ, если пользователь выбирает файл, а затем повторно выбирает файл (но затем отменяет), он будет рассматриваться как не выбранный файл, я использую только предупреждение в переменной selected_file_name для его проверки. во всяком случае, с тех пор больше не тестировал.
Rizky

4
Я могу сказать, после некоторого кроссбраузерного тестирования, что Firefox не запускает событие изменения при отмене, а также не очищает существующие файлы, выбранные при повторном открытии и нажатии кнопки отмены во второй раз. Странно, а?
mix3d

7

Большинство из этих решений мне не подходят.

Проблема в том, что никогда не знаешь, какое событие сработает кулаком, это 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);
    };
});

Если щелкнуть по странице ... «Отмена нажата» ... = | (Проверено с использованием Chrome)
Eduardo Lucio

5

Просто послушайте также событие щелчка.

Следуя примеру 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


1
Я тестировал Chrome 51, он не работает при первом выборе файлов. Решение - добавить задержку: jsfiddle.net/7jfbmunh
Бенуа Бланшон

Ваша скрипка у меня не работает - я использую Firefox 58.0.2
barrypicker

4

Вы можете поймать отмену, если выберете тот же файл, что и раньше, и нажмете кнопку «Отмена»: в этом случае.

Сделать это можно так:

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

1
Thnkx @loveJs, этот пост мне действительно помог.
VPK

4
Не совсем так, если пользователь выбирает тот же файл, это не отмена = (
Tiago Peres França

13
onchange срабатывает, только если было изменение. Таким образом, если файл является тем же файлом, что и предыдущий, прослушиватель onchange не сработает.
Шейн

1
Вы можете очистить выбор ввода (input.value = '') после обнаружения первого изменения, так что второй раз onchange сработает, даже если пользователь снова выберет тот же файл.
Крис

1
@CodeGems разрешено программно устанавливать input.value в пустую строку. Но вы правы, что установка любого другого значения не допускается.
Крис

4

Самый простой способ - проверить, есть ли файлы во временной памяти. Если вы хотите получать событие изменения каждый раз, когда пользователь нажимает на ввод файла, вы можете запустить его.

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

2

Решение 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 для индикатора активности, который будет отображаться в начало процесса.


2

Я нашел этот атрибут, пока он самый простой.

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

Вот selectedFileфайл input type=file.


Это хорошее решение для меня, за исключением того, что оно должно быть> = 1
Bron Thulke

2

Я знаю, что это очень старый вопрос, но на случай, если он кому-то поможет, я обнаружил, что при использовании события onmousemove для обнаружения отмены необходимо было проверить два или более таких события за короткий промежуток времени. Это произошло потому, что отдельные события onmousemove генерируются браузером (Chrome 65) каждый раз, когда курсор перемещается из диалогового окна выбора файла и каждый раз, когда он перемещается из главного окна и обратно. Простой счетчик событий движения мыши в сочетании с коротким таймаутом для сброса счетчика до нуля сработало.


1
Первый абзац - хороший ответ, даже если его можно разбить на несколько для лучшей читаемости. Но эти две строки «SOAPBOX» делают его граничным с «Неответом», поскольку вопросы не входят в ответы. Можете ли вы немного перефразировать / перефразировать свой ответ? Спасибо.
Филнор

1

В моем случае мне пришлось скрыть кнопку отправки, пока пользователи выбирали изображения.

Вот что я придумываю:

$(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. Примите во внимание эту информацию, если хотите ее реализовать.


1

Объединив решения 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 помогает, но если пользователь сделал щелчок и затем:

  • отменил диалог открытия файла клавишей ESC (без движения мыши), сделал еще один точный щелчок, чтобы снова открыть диалог файла ...
  • переключил фокус на любое другое приложение, затем вернулся в диалоговое окно открытия файла браузера и закрыл его, а затем снова открыл с помощью клавиши ввода или пробела ...

... мы не получим событие mousemove, следовательно, обратного вызова отмены не будет. Более того, если пользователь отменяет второй диалог и перемещает мышь, мы получим 2 обратных вызова отмены. К счастью, специальное событие jQuery focusIn всплывает в документе в обоих случаях, помогая нам избежать таких ситуаций. Единственное ограничение - блокировка события focusIn.


Я поймал mousemoveсобытие documentво время выбора файлов в диалоге ОС в Chrome 56.0.2924.87 на Ubuntu 16.10. Нет проблем, если вы не перемещаете мышь или используете только клавиатуру для выбора файла. Мне пришлось заменить mousemoveна, keydownчтобы обнаружить отмену как можно раньше, но не «слишком рано», когда диалог еще был открыт.
Фердинанд

1

Я вижу, что мой ответ был бы довольно устаревшим, но тем не менее. Я столкнулся с той же проблемой. Итак, вот мое решение. Самый полезный фрагмент кода - это код 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);
});

1

Вы можете обнаружить это только в ограниченных обстоятельствах. В частности, в Chrome, если файл был выбран ранее, а затем щелкнули диалоговое окно с файлом и нажали кнопку отмены, Chrome очищает файл и запускает событие onChange.

https://code.google.com/p/chromium/issues/detail?id=2508

В этом сценарии вы можете обнаружить это, обработав событие onChange и проверив свойство files .


1

Для меня работает следующее (на рабочем столе, в 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.


1

Поле file-type, к сожалению, не реагирует на множество событий (размытие было бы прекрасно). Я вижу много людей, предлагающих changeориентированные на решения решения, и за них голосуют против.

changeдействительно работает, но у него есть серьезный недостаток (по сравнению с тем, что мы хотим ).

Когда вы только что загружаете страницу, содержащую поле файла, откройте окно и нажмите «Отмена». К сожалению, ничего не меняется .

Я выбрал загрузку в закрытом состоянии.

  • Следующая часть формы a section#after-imageв моем случае скрыта от просмотра. Когда мои file fieldизменения, отображается кнопка загрузки. После успешной загрузки section#after-imageотображается.
  • Если пользователь загружается, открывает диалоговое окно с файлом, а затем отменяет его, он никогда не видит кнопку загрузки.
  • Если пользователь выбирает файл, отображается кнопка загрузки. Если они затем откроют диалоговое окно и отменит, changeсобытие запускается этой отменой, и там я могу (и действительно) повторно скрыть свою кнопку загрузки, пока не будет выбран правильный файл.

Мне повезло, что это закрытое состояние уже было дизайном моей формы. Вам не нужно использовать один и тот же стиль, просто сначала скрывая кнопку загрузки, а после изменения устанавливайте скрытое поле или переменную javascript на то, что вы можете отслеживать при отправке.

Я попытался изменить значение files [0] до взаимодействия с полем. Это не повлияло на изменение.

Так что да, changeработает, по крайней мере, так хорошо, как мы собираемся получить. Файловое поле защищено по очевидным причинам, но к разочарованию разработчиков с благими намерениями.

Это не соответствует моей цели, но вы могли бы onclickзагрузить предупреждение (не alert(), потому что это останавливает обработку страницы) и скрыть его, если срабатывает изменение и files [0] имеет значение null. Если изменение не запускается, div остается в своем состоянии.


0

Есть хакерский способ сделать это (добавить обратные вызовы или разрешить некоторую отложенную / обещающую реализацию вместо 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).

Но, конечно, это ненадежный способ обнаружить отмененный диалог. Хотя это может улучшить поведение пользовательского интерфейса для вас.


Один из основных недостатков - телефоны и планшеты, они обычно не используют мышь!
Питер

0

Вот мое решение, использующее фокус ввода файла (без использования таймеров)

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 }
Connexo 06

0

В лучшем случае это хакерство, но вот рабочий пример моего решения, чтобы определить, загрузил ли пользователь файл или нет, и разрешить им продолжить, только если они загрузили файл.

В основном скрыть 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 во многом это делается для того, чтобы сделать сайт и кнопку доступными для всех. Настройте свою кнопку так, как вам нравится.


0

Что ж, это не совсем ответ на ваш вопрос. Я предполагаю, что у вас есть сценарий, когда вы добавляете ввод файла и вызываете выбор файла, и если пользователь нажимает кнопку отмены, вы просто удаляете ввод.

Если это так, то: Зачем вводить пустой файл?

Создавайте один на лету, но добавляйте его в 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/


0

// Используем наведение вместо размытия

var fileInput = $("#fileInput");

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

    //open
} else {

}

0

Если вам уже требуется 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();

});


0

В этом потоке есть несколько предлагаемых решений, и эта трудность определения момента, когда пользователь нажимает кнопку «Отмена» в поле выбора файла, является проблемой, которая затрагивает многих людей.

Дело в том, что не существует 100% надежного способа определить, нажал ли пользователь кнопку «Отмена» в поле выбора файла. Но есть способы надежно определить, добавил ли пользователь файл во входной файл. Итак, это основная стратегия этого ответа!

Я решил добавить этот ответ, потому что, по-видимому, другие ответы не работают в большинстве браузеров и не гарантируются на мобильных устройствах.

Вкратце код основан на 3 пунктах:

  1. Входной файл изначально создается динамически в «памяти» в js (в данный момент мы не добавляем его в «HTML»);
  2. После добавления файла входной файл добавляется в HTML, иначе ничего не происходит;
  3. Удаление файла осуществляется путем удаления входного файла из HTML по определенному событию, что означает, что «редактирование» / «модификация» файла выполняется путем удаления старого входного файла и создания нового.

Для лучшего понимания посмотрите на приведенный ниже код и примечания.

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


0

Мы достигли углового, как показано ниже.

    1. привязать событие щелчка к файлу входного типа.
      1. Прикрепите событие фокуса к окну и добавьте условие, если uploadPanel истинно, затем покажите console.
      2. при нажатии на файл типа ввода логическое значение uploadPanel истинно. и появится диалоговое окно.
      3. при отмене ИЛИ нажмите кнопку Esc, появится диалоговое окно диспансера и консоль.

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

0

Просто добавьте в свой ввод слушателя изменений, тип которого - файл. т.е.

<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()вызывается ненадежно, если пользователь нажимает кнопку «Отмена» в средстве выбора файлов.
Бен Уиллер,

@benWheeler, я проверил его как на chrome, так и на firefox, и он работал, но не тестировался в других браузерах.
Навид Али,

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>

Ответы, состоящие только из кода, считаются низкокачественными: обязательно объясните, что делает ваш код и как он решает проблему. Если вы добавите дополнительную информацию в свой пост, это поможет как спрашивающему, так и будущим читателям. См. « Объяснение ответов на основе кода»
Калос,

0

Решение для выбора файла со скрытым вводом

Примечание: этот код не обнаруживает отмену, он предлагает способ обойти необходимость ее обнаружения в общем случае, когда люди пытаются ее обнаружить.

Я пришел сюда, когда искал решение для загрузки файлов с использованием скрытого ввода, я считаю, что это наиболее распространенная причина искать способ обнаружения отмены ввода файла (диалоговое окно открытия файла -> если файл был выбран, запустите несколько код, иначе ничего не делать), вот мое решение:

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

В chrome 83.0 я не получаю обратного вызова для события ввода, когда пользователь отменяет, и не при второй попытке
Сойлент Грэм

1
@SoylentGraham Да, поскольку этот код не обнаруживает отмену, он предлагает способ обойти необходимость его обнаружения в общем случае, когда люди пытаются его обнаружить. Поскольку я не совсем понял это, я добавляю этот комментарий к своему ответу.
картошка

Мне плохо, я неправильно истолковал ваш ответ, поскольку «он говорит вам, что пользователь отказался со второй попытки»
Сойлент Грэм

-1

Вы можете создать прослушиватель изменений 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
    }
});

Понятия не имею, почему это отклонено - это то, что я искал. Спасибо! 🙂
user1274820
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.