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

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

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



답변

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

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 솔루션을 알아낼 수있을 것입니다.


답변

당신은 할 수 없습니다.

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


답변

그래서 나는 새로운 해결책을 내놓았 기 때문에이 질문에 내 모자를 던질 것입니다. 사용자가 사진과 비디오를 캡처하고 업로드 할 수있는 프로그레시브 웹 앱이 있습니다. 가능한 경우 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는 기본적으로 모바일 장치에서 사용할 수 없으며 비교적 간단한 브라우저 변경으로 웹 개발자가 * 비눗 방울에서 벗어날 수 있습니다 *.


답변

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

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


답변

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


답변

이러한 솔루션의 대부분은 저에게 적합하지 않습니다.

문제는 어떤 이벤트가 가장 먼저 발생하는지 알 수 없다는 것 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);
    };
});


답변

클릭 이벤트도 들어보세요.

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