ファイル入力でキャンセルがクリックされたときに検出する方法は?


112

ユーザーがHTMLファイル入力を使用してファイル入力をキャンセルしたことをどのように検出できますか?

onChangeを使用すると、いつファイルを選択したかを検出できますが、いつキャンセルしたかを知りたいです(何も選択せずにファイル選択ダイアログを閉じます)。


また、誰かがファイルを選択したかどうかを検出して、送信を選択しようとしてもキャンセルできます。空になりますe.target.files
jcubic

回答:


45

直接的な解決策ではありませんが、(私がテストした限り)onfocusでのみ機能する(かなり限定的なイベントブロッキングが必要)ので、次のようにしてそれを実現できます。

document.body.onfocus = function(){ /*rock it*/ }

これの良い点は、ファイルイベントに合わせてアタッチまたはデタッチできることです。また、非表示の入力でも問題なく動作するようです(不快なデフォルトの入力type = 'に視覚的な回避策を使用している場合は確実な特典ですファイル')。その後、入力値が変更されたかどうかを確認する必要があります。

例:

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

3
これは、FirefoxとEdgeでは機能しません。ただし、mousemoveイベントをリッスンするwindow.document だけでなく、イベントをリッスンすることfocuswindowどこでも機能するようです(少なくとも最近のブラウザでは、過去10年の残骸は気にしません)。基本的に、開いているファイルを開くダイアログによってブロックされるこのタスクには、任意の対話イベントを使用できます。
John Weisz 2017

@WoodenKitty addEventListenerを使用してこれをどのように実装しましたか。私はこれを鋭角にしようとしています、そしてdocument.bodyも私のために働いていません
Terrance Jackson

これは非表示の入力フィールドでは機能しません。
セバスチャン

20

できません。

ファイルダイアログの結果はブラウザに公開されません。


ファイルチューザーが開いているかどうか確認できますか?
Ppp、2013年

1
@Ppp-いいえ。ブラウザはそれを伝えません。
Oded

6
「ファイルダイアログの結果はブラウザに公開されません。」これは、ファイルを選択してダイアログを閉じた場合には当てはまりません。
Trevor

3
変更ファイルダイアログを使用してトリガをブラウザにさらされています。ファイルが選択されておらず、その後ファイルも選択されていないchange場合は、何も変更されていないため、イベントは送出されません。
John Weisz 2017

1
また、ファイルが既に選択されている場合に同じファイルを再度選択すると、そのchangeイベントも送出されません。
パトリックロバーツ

16

私は新しい解決策を思いついたので、私はこの質問に私の帽子を投げます。ユーザーが写真やビデオをキャプチャしてアップロードできるプログレッシブWebアプリがあります。可能な場合はWebRTCを使用しますが、サポートが少ない* sough Safari cough *デバイスのHTML5ファイルピッカーにフォールバックします。特にネイティブカメラを使用して写真/ビデオを直接キャプチャするAndroid / iOSモバイルWebアプリケーションで作業している場合、これは私が遭遇した最良のソリューションです。

この問題の核心は、ページのロード時に、ということでfileあるnullが、ユーザが対話し、プレスを開いたときに「キャンセル」、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ミリ秒待機することで、この問題の回避策となる場合があります。

デスクトップ

残念ながら、これはデスクトップブラウザでは実際には機能しません。理論的には、バックグラウンドページよりも現在のページを優先するので、同じトリックが可能です。ただし、多くのデスクトップには、バックグラウンドでもページをフルスピードで実行し続けるのに十分なリソースがあるため、この戦略は実際には機能しません。

代替ソリューション

私が探求したと多くの人が言及していない代替ソリューションの1つは、をあざけることでしたFileList。私たちは、で始まるnull<input />、ユーザーがカメラを開き、彼らが戻ってくるまでキャンセルした場合、その後とnull変更なしイベントではありませんこれは、トリガされます。1つの解決策は<input />、ページ開始時にダミーファイルを割り当てることです。nullと、適切なイベントをトリガーする変更になります。

残念ながら、を作成する公式な方法はありませんFileList。また、<input />要素にはFileList特にが必要であり、以外の値は受け入れられませんnull。当然のことながら、FileListオブジェクトを直接構築することはできません。明らかに関連がなくなった古いセキュリティ問題に対処します。<input />要素の外側で1つを取得する唯一の方法は、データをコピーして貼り付けるハックを利用して、FileListオブジェクト偽造することです(基本的には、ファイルのドラッグアンドドロップを偽造しています) -your-website event)。これはFirefoxでは可能ですが、iOS Safariでは不可能であるため、私の特定のユースケースでは実行できませんでした。

ブラウザ、お願い...

言うまでもなく、これは明らかにばかげています。重要なUI要素が変更されたというWebページへの通知がゼロであるという事実は、ただ笑えるだけです。これは実際には仕様のバグです。フルスクリーンのメディアキャプチャUIを意図したものではなく、「変更」イベントをトリガーしないことは技術的にはに仕様に基づいています。

しかし、ブラウザのベンダーはこれの現実を認識できますか?これは、変更が発生しない場合でもトリガーされる新しい「done」イベントで解決するか、とにかく「change」をトリガーするだけで解決できます。ええ、それは仕様に反しますが、JavaScript側で変更イベントを重複排除するのは簡単ですが、自分の「完了」イベントを発明することは基本的に不可能です。ブラウザの状態が保証されていない場合でも、私の解決策は本当にヒューリスティックです。

現状では、このAPIは基本的にモバイルデバイスでは使用できません。ブラウザを比較的簡単に変更するだけで、Web開発者が* Soap Box *を使用するのが非常に簡単になると思います。



7

ファイルを選択して[開く/キャンセル]をクリックすると、input要素はフォーカスを失います(別名)blur。のイニシャルが空valueであると仮定すると、ハンドラーのinput空でない値はすべてblurOKを示し、空の値はキャンセルを意味します。

更新:が非表示のblur場合、inputはトリガーされません。したがって、一時的にを表示したい場合を除き、IFRAMEベースのアップロードでこのトリックを使用することはできませんinput


3
Firefox 10ベータおよびChrome 16ではblur、ファイルを選択してもトリガーされません。他のブラウザでは試していません。
ブレーズ

1
それは機能しません-入力のぼかしはクリック直後にトリガーされます。Win7上のChrome 63.0.3239.84 x64。
Qwertiy 2017

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 16

私がこの回答を書いたときに覚えている限り、ユーザーがファイルを選択してからファイルを再選択した場合(ただし、キャンセルした場合)は、ファイルが選択されていないものとして扱われ、selected_file_name変数でアラートを使用してテストします。とにかく、それ以来それをもうテストしていません。
リズキー2017年

4
いくつかのクロスブラウザーテストの後、Firefoxはキャンセル時に変更イベントを発生させず、再度開いてキャンセルをクリックした場合に選択された既存のファイルをクリアしないとも言えます。おかしい、え?
mix3d 2017年

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

あなたのフィドルが私のために動作しませんでした- 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 PeresFrançaNov

13
onchangeは、変更があった場合にのみ起動します。そのため、ファイルが以前と同じファイルである場合、onchangeリスナーは起動しません。
シェーン

1
最初の変更を検出した後、入力選択(input.value = '')をクリアして、ユーザーが同じファイルを再度選択した場合でも2回目の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のソリューションは、モバイルWebキットで機能すれば良いものですが、そうではありません。私が思いつくことができるのは、次のように、ファイル入力ウィンドウが開いているときに、いくつかのdomオブジェクトにmousemoveイベントリスナーを追加することです。

 $('.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以上である必要があります
ブロントゥルク、2017年

2

私はこれが非常に古い質問であることを知っていますが、誰かを助けるために、onmousemoveイベントを使用してキャンセルを検出すると、短時間でそのようなイベントを2つ以上テストする必要があることがわかりました。これは、カーソルがファイル選択ダイアログウィンドウの外に移動するたび、およびメインウィンドウの外に移動するたびに、ブラウザー(Chrome 65)によって1つのonmousemoveイベントが生成されるためです。マウス移動イベントの単純なカウンターカウンターをゼロにリセットする短い期間のタイムアウトと相まって、ごちそうになりました。


1
最初の段落は、読みやすくするために複数に分割することもできますが、良い答えです。しかし、これらの2つの「SOAPBOX」行は、質問が回答に属していないため、非回答の境界線になります。少し答えを言い換えたり、言い換えたりできますか?ありがとうございました。
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フォーカスが戻ったので、そのイベントをリッスンします。

更新

私は、これはサファリとポルターガイスト/ファントムで動作しないことを発見しました。実装する場合は、この情報を考慮してください。


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イベントはトリックを実行しますが、ユーザーがクリックした場合は、次のようになります。

  • (マウスを動かさずに)エスケープキーでファイルを開くダイアログをキャンセルし、もう一度正確にクリックしてファイルのダイアログをもう一度開きます...
  • フォーカスを他のアプリケーションに切り替え、ブラウザのファイルを開くダイアログに戻って閉じ、Enterキーまたはスペースキーでもう一度開いた...

... mousemoveイベントを取得しないため、キャンセルコールバックはありません。さらに、ユーザーが2番目のダイアログをキャンセルしてマウスを動かすと、2つのキャンセルコールバックが返されます。幸いなことに、特別なjQuery focusInイベントがどちらの場合もドキュメントにバブルアップし、そのような状況を回避するのに役立ちます。唯一の制限は、いずれかがfocusInイベントをブロックする場合です。


Ubuntu 16.10上のChrome 56.0.2924.87のOSダイアログでファイルを選択mousemoveしているdocumentときにイベントをキャッチしました。マウスを動かさない場合、またはキーボードだけを使用してファイルを選択する場合は問題ありません。ダイアログを開いたまま、キャンセルをできる限り早く検出するために、「早すぎる」ではなく、に置き換える必要がありましmousemovekeydown
Ferdinand Prantl 2017

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では、以前にファイルを選択してからファイルダイアログをクリックしてキャンセルをクリックすると、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このキャンセルによってイベントがトリガーされます。適切なファイルが選択されるまで、アップロードボタンを非表示にすることができます(実際に行うことができます)。

幸運にも、このゲート状態はすでに私のフォームのデザインでした。同じスタイルを使用する必要はありません。最初にアップロードボタンを非表示にし、変更時に非表示フィールドまたはJavaScript変数を送信時に監視できるものに設定するだけです。

フィールドが操作される前に、files [0]の値を変更してみました。これはonchangeに関しては何もしませんでした。

ですからchange、少なくとも私たちが手に入れようとしているように、うまくいきます。filefieldは、明らかな理由で保護されていますが、善意の開発者のフラストレーションに対して保護されています。

これは私の目的には合いませんが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のみ)。

しかし、これはもちろんキャンセルされたダイアログを検出する信頼できる方法ではありません。ただし、UIの動作が改善される場合があります。


大きな欠点の1つは携帯電話とタブレットで、通常はマウスを使用しません。
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

これはせいぜいハックですが、これは実際の例ですユーザーがファイルをアップロードしたかどうかを検出し、ファイルをアップロードした場合にのみ続行することを許可する私のソリューションのです。

基本的には非表示ContinueSaveProceedまたは任意のあなたのボタンがあります。次に、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の多くは、Webサイトとボタンに誰もがアクセスできるようにすることです。ボタンのスタイルを自由に設定できます。


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" />をその場で作成し、そのchangeイベントにバインドして、すぐにクリックを呼び出します。変更時は、ユーザーがファイルを選択して[OK]をクリックした場合にのみ起動します。それ以外の場合、入力は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%信頼できる方法はありません。しかし、ユーザーがファイルを追加したかどうか確実に検出する方法があります入力ファイルにをます。これがこの回答の基本戦略です!

他の回答がほとんどのブラウザで機能しないか、モバイルデバイスで保証されていないため、この回答を追加することにしました。

簡単に言うと、コードは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が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()ユーザーがファイルピッカーで[キャンセル]をクリックすると、確実に呼び出されません。
ベンウィーラー

@benWheeler、私はそれをchromeとfirefoxの両方で確認しましたが、他のブラウザでは動作しましたが、テストされていません。
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'));
   });
}

使用例:

ファイルが選択されていない場合、最初の行は1回だけ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はい、このコードはキャンセルを検出しないため、人々がそれを検出しようとする一般的なケースでそれを検出する必要性を回避する方法を提供します。よくわからなかったので、このコメントを回答に追加します。
ジャガイモ

私の悪いことだと思いますが、「2回目の試行でユーザーがキャンセルされたことがわかります」と答えを読み間違えました
Soylent Graham

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