web-dev-qa-db-ja.com

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

ユーザーがhtmlファイル入力を使用してファイル入力をキャンセルしたことを検出するにはどうすればよいですか?

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

94
Ppp

直接的な解決策ではありませんが、(テストした限り)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ソリューションを理解できるかもしれません

41
Shiboe

できません。

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

17
Oded
/* 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 */
    }
});
7
Rizky K.

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

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

6
zvolkov

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

この問題の核心は、ページがロードされるとき、filenullですが、ユーザーがダイアログを開いて[キャンセル]を押すと、filenull、したがって「変更」されなかったため、「変更」イベントはトリガーされません。デスクトップでは、ほとんどのデスクトップUIはキャンセルがいつ呼び出されるかを知ることに依存していないため、これはそれほど悪くありませんが、写真/ビデオをキャプチャするためにカメラを起動するモバイルUIはveryに依存していますキャンセルが押されたときを知る。

もともとdocument.body.onfocusイベントを使用して、ユーザーがファイルピッカーから戻ったタイミングを検出しましたが、これはほとんどのデバイスで機能しましたが、iOS 11.3ではそのイベントがトリガーされないため壊れました。

概念

これに対する私の解決策は、CPUタイミングを測定してページが現在フォアグラウンドにあるかバックグラウンドにあるかを判断する* shudder *です。モバイルデバイスでは、現在フォアグラウンドにあるアプリに処理時間が与えられます。カメラが表示されると、CPU時間を盗み、ブラウザの優先順位を下げます。必要なのは、ページが処理される時間を測定することです。カメラが起動すると、利用可能な時間が大幅に減少します。カメラが取り消されると(キャンセルまたはその他の理由で)、利用可能な時間が急上昇します。

実装

setTimeout()を使用してXミリ秒でコールバックを呼び出し、実際に呼び出すまでにかかった時間を測定することにより、CPUタイミングを測定できます。ブラウザはXミリ秒後にそれをexactly呼び出すことはありませんが、近い場合はフォアグラウンドにいる必要があります。ブラウザが非常に離れている場合(要求よりも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をモックすることです。 <input />nullから開始し、ユーザーがカメラを開いてキャンセルすると、nullに戻ります。これは変更ではなく、イベントはトリガーされません。 1つの解決策は、ページ開始時に<input />にダミーファイルを割り当てることです。したがって、nullに設定すると、適切なイベントをトリガーする変更になります。

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

ブラウザ、お願いします...

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

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

現状では、このAPIはモバイルデバイスでは基本的に使用できません。比較的単純なブラウザーの変更により、Web開発者はこれを限りなく簡単にできるようになります。

6
Douglas Parker

クリックイベントも聞くだけです。

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

5
AEQ

最も簡単な方法は、一時メモリにファイルがあるかどうかを確認することです。ユーザーがファイル入力をクリックするたびに変更イベントを取得したい場合は、トリガーできます。

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

これらのソリューションのほとんどは、私にとってはうまくいきません。

問題は、どのイベントが最初にトリガーされるかわからないということです。それはclickですか、それともchangeですか?おそらくブラウザの実装に依存するため、順序を想定することはできません。

少なくともOperaおよびChrome(2015年後半)clickは、入力をファイルで「埋める」直前にトリガーされるため、遅延するまで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);
    };
});
4
superdario128

以前と同じファイルを選択し、キャンセルをクリックすると、キャンセルをキャッチできます。この場合。

次のようにできます:

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

Shiboeのソリューションは、モバイルWebキットで機能した場合は良いソリューションになりますが、機能しません。私が思いつくのは、次のように、ファイル入力ウィンドウが開かれたときに、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
ransomweaver

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

2
Steve Brooker

これは限られた状況でのみ検出できます。具体的には、chromeで、以前にファイルが選択され、ファイルダイアログがクリックされ、キャンセルがクリックされた場合、Chromeはファイルをクリアし、onChangeイベントを発生させます。

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

このシナリオでは、onChangeイベントを処理し、filesプロパティをチェックすることでこれを検出できます。

1
Kunal

私の回答はかなり時代遅れになっていると思いますが、それでも決して少なくはありません。私は同じ問題に直面しました。だからここに私の解決策があります。切り取った最も有用なコードは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);
});

イライラするfile- typeフィールドは、多くのイベントに応答しません(ぼかしが素敵です)。多くの人がchange指向のソリューションを提案しており、彼らは支持を失っています。

changeは機能しますが、重大な欠陥があります(vs wantが起こることに対して)。

ファイルフィールドを含むページを新たにロードしたら、ボックスを開いてキャンセルを押します。イライラすることはありませんが、変更

私が選択したのは、ゲート状態でのロードです。

  • 私の場合、フォームのsection#after-imageの次の部分は非表示になっています。 file fieldが変更されると、アップロードボタンが表示されます。アップロードが成功すると、section#after-imageが表示されます。
  • ユーザーが読み込んでファイルダイアログを開き、キャンセルすると、アップロードボタンは表示されません。
  • ユーザーがファイルを選択すると、アップロードボタンが表示されます。その後、ダイアログを開いてキャンセルすると、このキャンセルによってchangeイベントがトリガーされます。適切なファイルが選択されるまで、アップロードボタンを再度非表示にすることができます。

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

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

そうです、changeは機能します。少なくとも私たちが得ようとしているのと同じくらい良いです。ファイルフィールドは、明らかな理由で保護されていますが、善意の開発者のフラストレーションに対応しています。

それは私の目的に合っていませんが、onclick、警告プロンプト(alert()ではなく、ページ処理を停止するため)をロードし、変更がトリガーされファイル[0]はヌルです。変更がトリガーされない場合、divはその状態のままになります。

1
Regular Joe

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イベントをブロックする場合です。

1
KGA

私の場合、ユーザーが画像を選択している間、送信ボタンを非表示にする必要がありました。

これは私が思いつくものです:

$(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はフォーカスを取り戻したので、そのイベントをリッスンします。

UPDATE

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

1
bonyiii

以下は私にとってはうまくいくようです(デスクトップ、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でテストしましたが、ChromeタブレットのAndroidでは動作しないようです。Focusイベントが発生しないためです。

1
Peter

すでに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
user2397355

ファイル入力フォーカスを使用する(タイマーを使用しない)私のソリューションは次のとおりです

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) {
    // ...
}
0
Yovav

//ブラーの代わりにホバーを使用

var fileInput = $("#fileInput");

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

    //open
} else {

}
0
jsil

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

基本的に、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
maxshuty

これを行うハック的な方法があります(コールバックを追加するか、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の動作が改善される場合があります。

0
alx

非表示入力を使用したファイル選択のソリューション

隠された入力を使用してファイルをアップロードするためのソリューションを探しているときにここに来ました、これはファイル入力のキャンセルを検出する方法を探す最も一般的な理由だと思います(ファイルを開くダイアログ->ファイルが選択されてからいくつかを実行するコード、それ以外は何もしません)、ここに私の解決策があります:

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

さて、これはあなたの質問に正確に答えているわけではありません。私の想定では、ファイル入力を追加し、ファイル選択を呼び出すシナリオがあり、ユーザーがキャンセルを押した場合、入力を削除するだけです。

この場合、空のファイル入力を追加するのはなぜですか?

オンザフライで作成しますが、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
Alexey