質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.11%

replaceWithでcloneしたinput type="file" 要素で再度、アップロードできない

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 3,478

score 183

一度、アップロードしたファイルについて、サムネイルを表示して、これをユーザーが削除し、
再びアッップロードできるコードをJSで書いております。

JSBIN

removeで削除し、replaceWithで要素を書き換えることができるのですが、改めてfileをアップロードするとサムネイルが表示されなくなってしまいます。
どの点に問題があるか解決ができなかったため質問させていただきます。
宜しくお願い申し上げます。

<!DOCTYPE html>
<html>
    <head>
        <title>XXX</title>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />

    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>



    </head>
    <body>


<section>
    <form class="" action="Z_confirm.php" method="post" enctype="multipart/form-data">

    <div class="fileUpload">
      <div id="drop_zone">
      <p style="font-size: 12px;">ここにファイルをドロップ</p>
      <p style="font-size: 10px;">または</p>
      <label for="files" class="fileupImg"><i class="plus icon"></i>ファイルを選択
      <input type="file" id="files" style="display: none" value="2097152" name="file" multiple />
      </label>
      </div>
     <figure>
     <output id="list"></output>
     </figure>
     <div class="fileInfo"></div>
    </div>

    <script>
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object

// Loop through the FileList and render image files as thumbnails.
for (var i = 0, f; f = files[i]; i++) {

// Only process image files.
if (!f.type.match('image.*')) {
continue;
}
var reader = new FileReader();

// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img class="thumbnail" src="', e.target.result,
'" title="', escape(theFile.name), '"/>'].join('');
document.getElementById('list').insertBefore(span, null);
};
})(f);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
</script>

    <script>  
                function handleDragOver(evt) {
                evt.stopPropagation();
                evt.preventDefault();
                evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
                }

                // Setup the dnd listeners.
                var dropZone = document.getElementById('drop_zone');
                dropZone.addEventListener('dragover', handleDragOver, false);
                dropZone.addEventListener('drop', handleFileSelect, false);

                dropZone.ondrop = function(e) {
                e.preventDefault();
                this.className = 'dropzone';
                console.log(e.dataTransfer.files);
                var fileInput = document.getElementById('files'); //fileのinputタグ取得
                var file = e.dataTransfer.files; //アップロード対象ファイル情報取得
                fileInput.files = file; //fileのinputタグへセット
                };

    </script>
<button type="button" name="button" id="delete" style="height:20px;">X</button>
<input class="submit step-button" type="submit" value="登録する">
</form>

</section>

            <script src="assets/js/jquery.min.js"></script>
            <script type="text/javascript">
            $(function(){
            $('#sidebar>a').css('color','#555555');
            });
            </script>

            <script type="text/javascript">
                $(function(){
                    $('#delete').on('click',function(){
                        $('#files').replaceWith($('#files').clone());
                        $('#list').find('span').remove();
                    })
                })
            </script>

    </body>
</html>
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • pegy

    2017/08/17 11:02

    コメント有難うございます。ファイルは誤って重複して記載してしまいました。大変失礼致しました、修正削除いたしました。また、エラー自体は簡略化するために生じているもので、本体とは関係がないとお考えください。jsbinのconsoleも同様です。

    キャンセル

  • kei344

    2017/08/17 11:07

    関数handleFileSelectが2つあるなど、「簡略化」の仕方に問題があるのでは。ひとまずエラーが出ずに状況が再現できるコードを提示されてはいかがでしょうか。

    キャンセル

  • pegy

    2017/08/17 12:39

    コメントありがとうございます。簡略化をせずにエラーのないコードに修正を致しました。

    キャンセル

回答 2

checkベストアンサー

+1

当方、生のJavascriptを書いたことがなくコードが理解できなかったのでJqueryのみで書いてみました。書いているうちに少し大きめになってしまいました。すみません。参考にしていただけますと幸いです。

[コード書き直しました]
ものすごく勘違いしてましたしっかり確認せずすみません。
問題は、ブラウザがfileをキャッシュするためchangeイベントが発生しないのが問題のようです。
コードを一部書き換えました勘違いしてましたすみません。

参考ページ

ポイントはこちらです。valueを削除しています。

el.input.prop('value','');


生のjavascriptならクリックイベントで

this.value = null;


でいけそうです。(未検証)

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
  </head>
<body>
  <form action="Z_confirm.php" method="post" enctype="multipart/form-data">

  <label for="files" class="fileupImg"><i class="plus icon"></i>ファイルを選択 
  <input type="file" id="files" style="display: none" value="2097152" name="file" multiple />
</label>
    </div>
    <figure>
    <output id="list"></output>
    </figure>
    <div class="fileInfo"></div>

  <button type="button" name="button" id="delete" style="height:20px;">X</button>
<br>
<br>
  <input class="submit step-button" type="submit" value="登録する">
</form>


  <script src="https://code.jquery.com/jquery-3.1.0.js"></script>
  <script type="text/javascript">
      jQuery(function()
      {
          // セットアップ
          var obj = form.fileSelect({
            input : '#files',
            list : '#list',
            deleteBtn : '#delete',
            thumbnail : { width : 150, class : 'thumbnail' },
            //複数のファイルを許可するか?
            multipleFile : true
          });

          // ファイルをセットした時に発火するイベント 引数に選択したファイルが返ってくる
          obj.OnSelectFile(function(file)
          {
            console.log(file);

            //ファイルが存在するか?
            console.log(obj.hasFile());

            //すべてのファイルを取得 配列
            console.log(obj.getFiles());
          });

          //すべてのファイルをリセット
          obj.reset();

      });
    </script>

    <script>
    var form;
    (function(g,$)
    {
      g.fileSelect = function(setting)
      {
        var el = {}, data = {};

        var init = function()
        {
          el.input = $(setting.input);
          el.list = $(setting.list);
          el.deleteBtn = $(setting.deleteBtn);

          data.files = [];

          setUp();
        };

        var setUp = function()
        {
          el.input.on('change', handleFileSelect);
          el.deleteBtn.on('click', deleteFile);
        };

        var hasFile = function() { return data.files.length > 0; }
        var getFiles = function() { return data.files; };

        var handleFileSelect = function(e)
        {
            var file = e.target.files[0],
                reader = new FileReader();

            if(file.type.indexOf("image") < 0) return false;

            reader.onload = (function(file)
            {
              return function(e)
              {
                if(!setting.multipleFile) reset();

                file.src = e.target.result;
                data.files.push(file);
                var spn = $('<span>');
                spn.append($('<img>').attr({
                          src: file.src,
                          width: setting.thumbnail.width,
                          class: setting.thumbnail.class,
                          title: file.name
                      }));

                el.list.append(spn);

                el.input.trigger( "OnSelectFile", file );

                reader.va
              };
            })(file);

            reader.readAsDataURL(file);
        };

        var deleteVal = function()
        {
          el.input.prop('value','');
        };

        var deleteFile = function()
        {
          el.list.html('');
          data.files = [];
          deleteVal();
        };

        var OnSelectFile = function(fn)
        {
          el.input.on('OnSelectFile', function(e, f){
            fn(f);
          });
        }

        var reset = function(){
          data.files = [];
          el.list.html('');
          deleteVal();
        };

        init();

        return {
          OnSelectFile : OnSelectFile,
          hasFile : hasFile,
          getFiles : getFiles,
          reset : reset
        }
      };
    }(form || (form = {}), jQuery));
    </script>
</body>
</html>

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/08/17 13:43

    ご回答ありがとうございます。また、細かいコードまでご提示頂き、御礼申し上げます。
    早速動かしてみたのですが、Aファイルをアップして、削除後にBファイルをアップする場合には機能するのですが、Aファイルをアップして、削除後に再びAファイルをアップすること(厳密にはサムネイルに表示すること)ができません。これはdeleteFileでも削除ができておらずコンフリクトしている等の原因によるものでしょうか、、もしアドバイスを頂けるとうれしいです。
    宜しくお願い申し上げます。

    キャンセル

  • 2017/08/17 14:40

    僕が勘違いしていたようです。申し訳ありません。
    コードを修正しましたのでご確認ください。
    よろしくお願いいたします。

    キャンセル

  • 2017/08/17 16:38

    IShikawan様
    とんでもないです、ご親切にご教示を頂き有難うございます。
    一旦ブラウザにfileがcashされると、新たにchangeイベントが起きても動作をしてくれないのですね。。
    Jqueryのコードにつきましては正しく動作いたしました!

    初心者で申し訳なく、最後に教えて頂きたいのですが
    cashしたファイルをvalue属性を削除することで、なぜクリアすることができるのでしょうか?
    inputを要素毎削除するのにvalueを個別に削除する点が調べても分かりませんでした。。

    また、this.value=null;の場合、input要素のvalue要素を削除したいということであればthisはinputエレメントを指しているとして.valueのこのプロパティは何を指しているのでしょうか。。。?

    キャンセル

+1

要素を差し替えていることによって、イベントリスナの設定が壊れているのだと思います。
対策としては、要素を差し替えない方式(たとえばdetachメソッドはイベントを壊しません)に切り替えるか、バブリングを拾う(参考)か、だと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/08/17 13:48

    コメント頂き有難うございます。原因については、ずっと考えていたため、ご教示頂きありがとうございます。removeさせずに、detachメソッドでは今のところうまく機能しなかったのですが、ハブリングを拾う方法も試した見ようと思います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.11%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る