파일 입력에서 취소를 클릭하면 어떻게 감지합니까?


112

사용자가 HTML 파일 입력을 사용하여 파일 입력을 취소하는 경우 어떻게 감지 할 수 있습니까?

onChange를 사용하면 파일을 선택할 때 감지 할 수 있지만 취소 할 때도 알고 싶습니다 (아무것도 선택하지 않고 파일 선택 대화 상자 닫기).


누군가가 파일을 선택하고 전송 하나를 선택하지만 취소하려고하면 당신은 또한, 검색 할 수 있습니다 당신은 빈 얻을 것이다e.target.files
jcubic

답변:


45

직접적인 솔루션은 아니지만 (내가 테스트 한 한) 온 포커스 (꽤 제한적인 이벤트 차단이 필요함)와 함께 작동한다는 점에서 나쁘지만 다음과 같이 달성 할 수 있습니다.

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/

슬프게도 웹킷 브라우저에서만 작동하는 것 같습니다. 아마도 다른 누군가가 firefox / IE 솔루션을 알아낼 수있을 것입니다.


매번 포커스 이벤트가 발생하는 것을 원하지 않았기 때문에 addEventListener("focus",fn,{once: true}). 그러나 나는 그것을 사용하여 전혀 발사 document.body.addEventListener하지 못했습니다 .. 나는 이유를 모르겠습니다. 대신 나는 window.addEventListener.
WoodenKitty

3
이것은 나를 위해 Firefox 및 Edge에서 작동하지 않습니다. 그러나 mousemove이벤트 를 듣는 것 window.document 외에도 이벤트 를 듣는 것은 모든 곳에서 작동 focus하는 window것 같습니다 (적어도 최신 브라우저에서는 지난 10 년 동안의 난파선에 대해 신경 쓰지 않습니다). 기본적으로 열려있는 파일 열기 대화 상자에 의해 차단 된이 작업에는 모든 상호 작용 이벤트를 사용할 수 있습니다.
John Weisz

@WoodenKitty addEventListener를 사용하여 이것을 어떻게 구현 했습니까? 나는 각도에서 이것을하려고하고 document.body도 나를 위해 작동하지 않습니다
Terrance Jackson

숨겨진 입력 필드에서는 작동하지 않습니다.
Sebastian

20

당신은 할 수 없습니다.

파일 대화 상자의 결과는 브라우저에 노출되지 않습니다.


파일 선택기가 열려 있는지 여부를 확인할 수 있습니까?
Ppp

1
@Ppp-아니요. 브라우저는이를 알려주지 않습니다.
Oded

6
"파일 대화 상자의 결과는 브라우저에 노출되지 않습니다." 파일을 선택하여 대화 상자를 닫은 경우에는 해당되지 않습니다.
Trevor

3
변경 파일 대화 상자를 사용하여 트리거가 브라우저에 노출된다. 선택한 파일이없고 이후에 파일이 선택되지 않으면 아무것도 변경되지 않았으므로 change이벤트가 전달 되지 않습니다 .
John Weisz

1
또한 파일이 이미 선택되어 있고 동일한 파일을 다시 선택하면 해당 파일에 대한 change이벤트도 전달 되지 않습니다 .
Patrick Roberts

16

그래서 나는 새로운 해결책을 내놓았 기 때문에이 질문에 내 모자를 던질 것입니다. 사용자가 사진과 비디오를 캡처하고 업로드 할 수있는 프로그레시브 웹 앱이 있습니다. 가능한 경우 WebRTC를 사용하지만 * cough Safari cough *를 지원하지 않는 장치의 경우 HTML5 파일 선택기로 대체합니다. 기본 카메라를 사용하여 사진 / 비디오를 직접 캡처하는 Android / iOS 모바일 웹 응용 프로그램에서 특별히 작업하는 경우 이것이 제가 본 최고의 솔루션입니다.

이 문제의 핵심은이 페이지가로드 될 때이다 filenull사용자가 대화 상자를 열고 프레스 후 때,하지만은, "취소 없다" file여전히 null따라서는 "변화"하지 않았다 그래서 "변경"이벤트가 트리거됩니다. 데스크톱의 경우 대부분의 데스크톱 UI는 취소가 호출되는시기를 아는 데 의존하지 않기 때문에 그렇게 나쁘지는 않지만 사진 / 비디오를 캡처하기 위해 카메라를 불러오는 모바일 UI 는 취소를 눌렀을 때를 아는 것에 매우 의존합니다.

원래이 document.body.onfocus이벤트를 사용하여 사용자가 파일 선택기에서 돌아 왔을 때이를 감지했으며 이는 대부분의 장치에서 작동했지만 해당 이벤트가 트리거되지 않아 iOS 11.3에서 중단되었습니다.

개념

이것에 대한 나의 해결책은 페이지가 현재 전경에 있는지 배경에 있는지 확인하기 위해 CPU 타이밍을 측정하는 * 떨림 *입니다. 모바일 장치에서 처리 시간은 현재 포 그라운드에있는 앱에 제공됩니다. 카메라가 보이면 CPU 시간을 훔치고 브라우저의 우선 순위를 낮 춥니 다. 우리가해야 할 일은 우리 페이지에 주어진 처리 시간을 측정하는 것뿐입니다. 카메라가 시작되면 사용 가능한 시간이 급격히 줄어들 것입니다. 카메라가 취소되면 (취소 또는 기타) 사용 가능한 시간이 다시 급증합니다.

이행

를 사용하여 setTimeout()X 밀리 초 안에 콜백을 호출 하여 CPU 타이밍 을 측정 한 다음 실제로 호출하는 데 걸린 시간을 측정 할 수 있습니다. 브라우저는 정확히 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" />

이것은 실제로 내가 예상했던 것보다 훨씬 잘 작동합니다. 25 밀리 초 안에 타이머를 호출하도록 요청하여 10 번의 시도를 실행합니다. 그런 다음 실제로 호출하는 데 걸린 시간을 측정하고 평균 10 회의 시도가 50 밀리 초 미만이면 전면에 있어야하고 카메라가 사라 졌다고 가정합니다. 50 밀리 초보다 크면 여전히 백그라운드에 있어야하며 계속 기다려야합니다.

추가 세부 정보

후자가 서로 즉시 실행되는 여러 호출을 대기열에 넣을 수 있기 때문에 setTimeout()대신 사용했습니다 setInterval(). 이로 인해 데이터의 노이즈가 급격히 증가 할 수 있으므로 setTimeout()조금 더 복잡하지만 고집 했습니다.

이 특정 숫자는 저에게 잘 맞았지만 카메라 해제가 조기에 감지 된 사례를 한 번 이상 본 적이 있습니다. 나는 카메라가 느리게 열릴 수 있고 장치가 실제로 배경이되기 전에 10 번의 평가판을 실행할 수 있기 때문이라고 생각합니다. 이 기능을 시작하기 전에 더 많은 시도를 추가하거나 약 25-50 밀리 초를 기다리면 해결 방법이 될 수 있습니다.

데스크탑

불행히도 이것은 데스크톱 브라우저에서 실제로 작동하지 않습니다. 이론적으로는 백그라운드 페이지보다 현재 페이지의 우선 순위를 지정하는 것과 동일한 트릭이 가능합니다. 그러나 많은 데스크톱에는 백그라운드 상태에서도 페이지를 최대 속도로 실행하는 데 충분한 리소스가 있으므로이 전략은 실제로 작동하지 않습니다.

대체 솔루션

많은 사람들이 언급하지 않은 대안 중 하나는 FileList. null에서 시작 <input />하여 사용자가 카메라를 열고 취소하면로 돌아갑니다 null. 이는 변경 사항이 아니며 이벤트가 트리거되지 않습니다. 한 가지 해결책은 <input />페이지 시작시 더미 파일을 할당하는 것이므로로 설정 null하면 적절한 이벤트를 트리거하는 변경이됩니다.

불행하게도, 거기를 만들 수있는 방법 공식 방법은 없습니다 FileList, 그리고 <input />요소는이 필요 FileList특히 및 이외의 다른 값을 허용하지 않습니다 null. 당연히 FileList객체는 직접 구성 할 수 없으며 더 이상 관련성이없는 오래된 보안 문제를 처리합니다. <input />요소 외부에서 하나를 확보하는 유일한 방법 은 데이터를 복사하여 붙여 넣는 해킹을 활용하여 FileList객체를 포함 할 수있는 클립 보드 이벤트를 가짜로 만드는 것입니다 (기본적으로 파일을 끌어서 놓기 -웹 사이트 이벤트). 이것은 Firefox에서 가능하지만 iOS Safari에서는 가능하지 않으므로 특정 사용 사례에는 적합하지 않았습니다.

브라우저, 제발 ...

말할 필요도없이 이것은 명백히 우스꽝 스럽습니다. 웹 페이지에 중요한 UI 요소가 변경되었다는 알림이 전혀 제공되지 않는다는 사실은 웃기만합니다. 이것은 전체 화면 미디어 캡처 UI를위한 것이 아니며 "변경"이벤트를 트리거하지 않는 것은 기술적 으로 사양 에 따른 것이기 때문에 사양의 버그입니다 .

그러나 브라우저 공급 업체가 이것의 현실을 인식 할 수 있습니까? 이 문제는 변경이 발생하지 않은 경우에도 트리거되는 새로운 "완료"이벤트로 해결 될 수 있으며, 어쨌든 "변경"을 트리거 할 수도 있습니다. 그래, 그것은 사양에 위배 될 것이지만, 자바 스크립트 측에서 변경 이벤트를 중복 제거하는 것은 사소한 일이지만, 내 자신의 "완료"이벤트를 발명하는 것은 근본적으로 불가능합니다. 내 솔루션조차도 브라우저 상태에 대한 보장을 제공하지 않는 경우 실제로 휴리스틱입니다.

현재이 API는 기본적으로 모바일 장치에서 사용할 수 없으며 비교적 간단한 브라우저 변경으로 웹 개발자가 * 비눗 방울에서 벗어날 수 있습니다 *.



7

파일을 선택하고 열기 / 취소를 클릭하면 input요소가 초점을 잃게됩니다 blur. 초기 가정 value의 것은 input비어, 당신의 아닌 빈 값 blur핸들러는 OK를 나타냅니다, 그리고 빈 값은 취소 의미 할 것입니다.

업데이트 : 가 숨겨져 blur있을 때는 트리거되지 않습니다 input. 따라서 IFRAME 기반 업로드에이 트릭을 사용할 수 없습니다.input .


3
Firefox 10 베타 및 Chrome 16에서는 blur파일을 선택할 때 트리거되지 않습니다. 다른 브라우저에서 시도하지 않았습니다.
Blaise

1
작동하지 않습니다. 입력시 블러가 클릭 직후 트리거됩니다. Win7의 Chrome 63.0.3239.84 x64.
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에서는 입력을 파일로 '채우기'직전에 트리거되므로 .NET Framework 이후에 트리거 files.length != 0될 때까지 지연 될 때까지 의 길이를 알 수 없습니다 .clickchange

다음은 코드입니다.

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

페이지를 클릭하면 ... "Cancel clicked"... = | (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
Benoit Blanchon

귀하의 바이올린 나를 위해 작동하지 않았다 - 파이어 폭스 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 2013 년

4
정확하지 않게, 사용자가 선택하는 것이 아니라 같은 파일은 취소하면 = (
티아구 페레스 프란카

13
onchange는 변경 사항이있는 경우에만 실행됩니다. 따라서 파일이 이전 파일과 동일한 파일이면 onchange 리스너가 실행되지 않습니다.
Shane

1
첫 번째 변경 사항을 감지 한 후에 입력 선택 (input.value = '')을 지울 수 있으므로 사용자가 동일한 파일을 다시 선택하더라도 두 번째 onchange가 실행됩니다.
Chris

1
@CodeGems는 input.value를 빈 문자열로 프로그래밍 방식으로 설정할 수 있습니다. 그러나 다른 값으로 설정하는 것은 허용되지 않습니다.
Chris

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의 솔루션은 모바일 웹킷에서 작동한다면 좋은 솔루션이 될 것이지만 그렇지 않습니다. 내가 생각 해낼 수있는 것은 파일 입력 창이 열릴 때 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 2017

2

나는 이것이 매우 오래된 질문이라는 것을 알고 있지만 누군가에게 도움이되는 경우 onmousemove 이벤트를 사용하여 취소를 감지 할 때 짧은 시간 내에 두 개 이상의 이러한 이벤트를 테스트해야한다는 것을 알았습니다. 이는 단일 onmousemove 이벤트가 브라우저 (Chrome 65)에 의해 커서가 파일 선택 대화 상자 창 밖으로 이동할 때마다 그리고 주 창 밖으로 이동했다가 다시 들어올 때마다 생성되기 때문입니다. 마우스 이동 이벤트의 간단한 카운터 카운터를 다시 0으로 재설정하는 짧은 시간 제한과 결합하여 치료를 받았습니다.


1
첫 번째 단락은 가독성을 높이기 위해 여러 단락으로 나눌 수 있지만 좋은 대답입니다. 그러나이 두 개의 "SOAPBOX"줄은 질문이 답변에 속하지 않기 때문에 Non-Answer와 경계를 이룹니다. 답변을 약간 바꾸거나 바꿀 수 있습니까? 감사합니다.
Filnor

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 이벤트가 트릭을 수행하지만 사용자가 클릭 한 경우

  • 이스케이프 키로 파일 열기 대화 상자를 취소했습니다 (마우스를 움직이지 않음). 파일 대화 상자를 다시 열려면 또 다른 정확한 클릭을했습니다.
  • 브라우저의 파일 열기 대화 상자로 돌아와 닫은 것보다 다른 응용 프로그램으로 포커스를 전환했습니다.

... mousemove 이벤트가 발생하지 않으므로 취소 콜백이 없습니다. 또한 사용자가 두 번째 대화 상자를 취소하고 마우스를 움직이면 2 개의 취소 콜백을 받게됩니다. 다행스럽게도 특별한 jQuery focusIn 이벤트는 두 경우 모두 문서에 버블 링되어 이러한 상황을 피하는 데 도움이됩니다. 유일한 제한은 하나가 focusIn 이벤트를 차단하는 경우입니다.


나는 잡힐 mousemove에 이벤트를 document우분투 16.10에 크롬 56.0.2924.87의 OS 대화 상자에서 파일을 선택하는 동안. 마우스를 움직이지 않거나 키보드를 사용하여 파일을 선택할 때 문제 없습니다. 나는 대체했다 mousemovekeydown(가) 가능한 한 빨리 취소 감지 할 수 있지만 "너무 일찍"대화 상자가 계속 열려있을 때.
Ferdinand Prantl

1

내 응답은 상당히 구식이지만 결코 덜하지 않습니다. 나는 같은 문제에 직면했다. 그래서 여기 내 해결책이 있습니다. 가장 유용한 코드는 KGA의 코드였습니다. 그러나 그것은 완전히 작동하지 않고 약간 복잡합니다. 그러나 나는 그것을 단순화했다.

또한 주된 문제는 '변화'사건이 집중 직후에 오지 않기 때문에 잠시 기다려야한다는 사실이었습니다.

"#appendfile"-새 파일을 추가하기 위해 클릭하는 사용자. Href는 포커스 이벤트를받습니다.

$("#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은 파일을 지우고 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를 사용하지만 필요한 경우 다른 promise 프레임 워크로 대체 할 수 있어야합니다.

IE11, Edge, Chrome, Firefox에서 테스트되었지만 Focus 이벤트를 발생시키지 않기 때문에 Android 태블릿의 Chrome에서 작동하지 않는 것 같습니다.


1

file타입 필드는, 실망스럽게도, 많은 이벤트 (흐림 사랑스러운 것)에 응답하지 않습니다. 나는 많은 사람들이 change지향적 인 해결책을 제안 하고 그들이 반대 투표를받는 것을 봅니다.

change작동하지만 큰 결함이 있습니다 ( 원하는 것 대비 것보다).

파일 필드가 포함 된 페이지를 새로로드하면 상자를 열고 취소를 누르십시오. 아무것도, 실망스럽게도, 변경 .

내가 선택한 것은 게이트 상태에서로드하는 것입니다.

  • section#after-image경우 에는 양식 a의 다음 부분 이 보이지 않습니다. 언제 file field변경, 업로드 버튼이 표시됩니다. 성공적으로 업로드되면 section#after-image이 표시됩니다.
  • 사용자가로드하고 파일 대화 상자를 연 다음 취소하면 업로드 버튼이 표시되지 않습니다.
  • 사용자가 파일을 선택하면 업로드 버튼이 표시됩니다. 그런 다음 대화 상자를 열고 취소하면 change이 취소에 의해 이벤트가 트리거되고 적절한 파일이 선택 될 때까지 업로드 버튼을 다시 숨길 수 있습니다.

나는이 문이있는 상태가 이미 내 형태의 디자인이라는 것이 다행이었다. 동일한 스타일을 사용할 필요가 없습니다. 업로드 버튼을 처음에 숨기고 변경시 숨겨진 필드 또는 자바 스크립트 변수를 제출시 모니터링 할 수있는 것으로 설정하기 만하면됩니다.

필드가 상호 작용하기 전에 files [0]의 값을 변경해 보았습니다. 이것은 onchange와 관련하여 아무것도하지 않았습니다.

그렇습니다. change적어도 우리가 얻을 수있는 것만 큼 좋습니다. 파일 필드는 명백한 이유로 보안이 유지되지만 선의의 개발자가 실망 할 수 있습니다.

내 목적에 맞지 않지만 onclick경고 프롬프트를로드하고 ( alert()페이지 처리가 중단되기 때문에이 아님 ) 변경이 트리거되고 files [0]이 null 인 경우 숨길 수 있습니다. 변경이 트리거되지 않으면 div는 해당 상태를 유지합니다.


0

이를 수행하는 hackish 방법이 있습니다 (콜백을 추가하거나 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);

작동 방식 : 파일 선택 대화 상자가 열려있는 동안 문서는 마우스 포인터 이벤트를받지 않습니다. 대화 상자가 실제로 나타나고 브라우저 창을 차단할 수 있도록 1000ms 지연이 있습니다. Chrome 및 Firefox에서 확인했습니다 (Windows 만 해당).

그러나 이것은 물론 취소 된 대화를 감지하는 신뢰할 수있는 방법이 아닙니다. 하지만 일부 UI 동작을 개선 할 수 있습니다.


한 가지 주요 단점은 휴대폰과 태블릿에서 마우스를 정상적으로 사용하지 않는다는 것입니다!
Peter

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

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>

자바 스크립트 :

$('#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가 필요한 경우이 솔루션이 작업을 수행 할 수 있습니다 (Promise를 사용하는 것은 파일 선택이 해결 될 때까지 코드를 대기하도록 강제하는 것이지만 제 경우에 실제로 필요한 코드와 똑같습니다).

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 % 신뢰할 수있는 방법은 없습니다 . 하지만 사용자가 입력 파일에 파일추가했는지 확실하게 감지하는 방법이 있습니다. 그래서 이것이이 답변의 기본 전략입니다!

다른 답변은 대부분의 브라우저에서 작동하지 않거나 모바일 장치에서 보장되지 않기 때문에이 답변을 추가하기로 결정했습니다.

간단히 코드는 세 가지 사항을 기반으로합니다.

  1. 입력 파일은 처음에 js의 "memory"에서 동적으로 생성됩니다 (현재 "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이 true이면 조건을 추가 한 다음 콘솔을 표시합니다.
      2. 입력 유형 파일을 클릭하면 부울 uploadPanel 값이 true입니다. 및 대화 상자가 나타납니다.
      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()사용자가 파일 선택기에서 "취소"를 클릭하면 안정적으로 호출되지 않습니다.
Ben Wheeler

@benWheeler, 나는 크롬과 파이어 폭스에서 모두 확인했고 다른 브라우저에서는 테스트하지 않았습니다.
Naveed Ali

0
    enter code here
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <h1>Hello</h1>
    <div id="cancel01">
      <button>Cancel</button>
    </div>

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

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

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

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

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

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

          /******
           *The below code will delete the file path
           ******/
          nameInput.value = '';
        }
      });
    </script>
  </body>
</html>

코드 전용 답변은 품질이 낮은 것으로 간주됩니다. 코드가 수행하는 작업과 문제를 해결하는 방법에 대한 설명을 제공해야합니다. 게시물에 더 많은 정보를 추가 할 수 있다면 질문자와 미래 독자 모두에게 도움이 될 것입니다. 완전한 코드 기반 답변 설명
Calos를

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

크롬 83.0에서는 사용자가 취소 할 때 입력 이벤트에 대한 콜백이없고 두 번째 시도도하지 않습니다
Soylent Graham

1
@SoylentGraham 예,이 코드는 취소를 감지하지 않기 때문에 사람들이 감지하려고하는 일반적인 경우에 감지 할 필요성을 피할 수있는 방법을 제공합니다. 나는 그것에 대해 명확하지 않았기 때문에이 의견을 내 대답에 추가하고 있습니다.
감자

"이 두번째 시도에서 취소하면 사용자 이야기"로 내가 생각하는 나의 나쁜, 나는 당신의 대답을 오해
Soylent 그레이엄

-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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.