アナログCPU:5108843109

ゲームと音楽とプログラミング(酒と女とロックンロールのノリで)

('ω') < イザユケエンジニャー

ドラッグ&ドロップでファイルをアップロードする(ちょっとリッチ版)

こちらの記事が前提となります。

簡単に書くと
「ファイルのアップロードフォーム」と
「アップロードしたいファイルをドロップできるエリア」を用意して
エリアにドロップされたファイルをフォームに渡す処理が既にあります。

これに対して、この記事では以下の処理を追加していきます。

  • ドロップしたら送信ボタンを押さなくてもアップロード
  • ファイル選択から選択したときもボタンを押さずにアップロード
  • ファイル選択のボタンとかかっこわるいから出さない。ドロップ用のエリアをクリックしたら選べるようにする
  • ドロップエリアにマウスが乗ってる間は色を変えるとか分かりやすくする
  • ドロップエリアの外でドロップしてもファイルが開かないようにする

これを実現するためにもうちょっと切り分けて、具体的には以下のような実装をします。

  • formを非表示にする
  • ドロップエリアをクリックしたら、formのファイル選択ボタンをクリックしたことにする
  • formのファイルinputタグが変更されたら、formの送信ボタンをクリックしたことにする
  • ドロップエリアの上にファイルを掴んで乗っかったときにエリアの色を薄くする
  • ドロップエリアでドロップされたときにエリアの色をデフォルト状態に戻す
  • ドロップエリアから離れたときにエリアの色をデフォルト状態に戻す
  • ドロップエリアの外でドロップしてもファイルが開かないようにする

こんな感じでしょうか。
順番にやっつけていきます。

formを非表示にする

これは簡単。formのstyleに「display: none;」を追加します。

<HTML>

<div id="upload-area" style="height: 100px; width: 100px; background: #DDD;">うp</div>
<form enctype="multipart/form-data" action="/sandbox/upload.php" method="post" style="display: none;">
  <input type="file" id="upload-form-fileselect" name="files[]" multiple="multiple">
  <input type="submit" value="送信">
</form>

ドロップエリアをクリックしたら、formのファイル選択ボタンをクリックしたことにする

ドロップエリアにonclickイベントを追加します。
JSで対応してもよいです。お好みで。

おまけにカーソルを指マークにするため、styleに「cursor: pointer;」を追加しておきました。

<HTML>

<div id="upload-area" style="height: 100px; width: 100px; background: #DDD; cursor: pointer;" onclick="$('#upload-form-fileselect').click();">
  うp<br>クリックもおk
</div>
<form enctype="multipart/form-data" action="/sandbox/upload.php" method="post" style="display: none;">
  <input type="file" id="upload-form-fileselect" name="files[]" multiple="multiple">
  <input type="submit" value="送信">
</form>

formのファイルinputタグが変更されたら、formの送信ボタンをクリックしたことにする

送信inputタグにIDを振り、ファイルinputタグにonchangeイベントを追加します。

<HTML>

<div id="upload-area" style="height: 100px; width: 100px; background: #DDD; cursor: pointer;" onclick="$('#upload-form-fileselect').click();">
  うp<br>クリックもおk
</div>
<form enctype="multipart/form-data" action="/sandbox/upload.php" method="post" style="display: none;">
  <input type="file" id="upload-form-fileselect" name="files[]" multiple="multiple" onchange="$('#upload-form-submit').click();">
  <input type="submit" id="upload-form-submit" value="送信">
</form>

ここで問題が。
エリアをクリックしてファイル選択したらすぐに送信されるのですが、
ドロップしても送信されない。
formタグを覗いてみると、セットはされているのに「onchange」に引っかからない模様。

ということで、JS側のdrop時処理に「changeしたぜ!」というのを明示的に入れてみました。動いた。

<JS>

$(document).ready(function(){
    // 指定部分の上に乗っかっているときの処理
    $("#upload-area").on("dragover", function(e){
        e.preventDefault();
    });

    // 指定部分でドロップしたときの処理
    $("#upload-area").on("drop", function(e){
        e.preventDefault();
        document.getElementById("upload-form-fileselect").files = e.originalEvent.dataTransfer.files;
        $("#upload-form-fileselect").change(); // ←ここ!
    });
});

お好みで、もっと直球に送信ボタンをクリックする処理にするのもアリかも。
ファイルinputタグに仕込んだonchangeと同じものですね。

$('#upload-form-submit').click();

ドロップエリアの上にファイルを掴んで乗っかったときにエリアの色を薄くする

ここで、dragenter処理を追加します。
もちろん背景色の変更をするだけ。

<JS>

$(document).ready(function(){
    // 指定部分にドラッグしたときの処理
    $("#upload-area").on("dragenter", function(e){
        $("#upload-area").css("background", "#EEE"); // ←ここ!
    });

    // 指定部分の上に乗っかっているときの処理
    $("#upload-area").on("dragover", function(e){
        e.preventDefault();
    });

    // 指定部分でドロップしたときの処理
    $("#upload-area").on("drop", function(e){
        e.preventDefault();
        document.getElementById("upload-form-fileselect").files = e.originalEvent.dataTransfer.files;
        $("#upload-form-fileselect").change();
    });
});

これだとエリアを離れても薄くなったままなので、戻す処理も加えていきます。

ドロップエリアでドロップされたときにエリアの色をデフォルト状態に戻す

drop処理内で色を戻します。
すぐページ遷移してしまう場合は必要ないかもしれませんが。

<JS>

$(document).ready(function(){
    // 指定部分にドラッグしたときの処理
    $("#upload-area").on("dragenter", function(e){
        $("#upload-area").css("background", "#EEE");
    });

    // 指定部分の上に乗っかっているときの処理
    $("#upload-area").on("dragover", function(e){
        e.preventDefault();
    });

    // 指定部分でドロップしたときの処理
    $("#upload-area").on("drop", function(e){
        e.preventDefault();
        document.getElementById("upload-form-fileselect").files = e.originalEvent.dataTransfer.files;
        $("#upload-form-fileselect").change();
        $("#upload-area").css("background", "#DDD"); // ←ここ!
    });
});

ドロップエリアから離れたときにエリアの色をデフォルト状態に戻す

dragleave処理を加え、同じことをします。

<JS>

$(document).ready(function(){
    // 指定部分にドラッグしたときの処理
    $("#upload-area").on("dragenter", function(e){
        $("#upload-area").css("background", "#EEE");
    });

    // 指定部分の上に乗っかっているときの処理
    $("#upload-area").on("dragover", function(e){
        e.preventDefault();
    });

    // 指定部分から離れたときの処理
    $("#upload-area").on("dragleave", function(e){
        $("#upload-area").css("background", "#DDD"); // ←ここ!
    });

    // 指定部分でドロップしたときの処理
    $("#upload-area").on("drop", function(e){
        e.preventDefault();
        document.getElementById("upload-form-fileselect").files = e.originalEvent.dataTransfer.files;
        $("#upload-form-fileselect").change();
        $("#upload-area").css("background", "#DDD");
    });
});

これでエリア内外でドラッグしても、きちんと色が切り替わるようになりました。

ドロップエリアの外でドロップしてもファイルが開かないようにする

うっかりエリア外にドロップしてしまったとき、ファイルが開かないようにする処理です。
とはいえエリア内にドロップしたときと同じことをするだけ。

<JS>

// ページ全体に乗っかっているときの処理
$(document).on("dragover", function(e){
    e.preventDefault();
});

// ページ全体でドロップしたときの処理
$(document).on("drop", function(e){
    e.preventDefault();
});

この処理を追加した場合、ドロップエリア部分での同処理は削除してよいので、以下のように整理できます。

<JS>

$(document).ready(function(){
    // 指定部分にドラッグしたときの処理
    $("#upload-area").on("dragenter", function(e){
        $("#upload-area").css("background", "#EEE");
    });

    // 指定部分から離れたときの処理
    $("#upload-area").on("dragleave", function(e){
        $("#upload-area").css("background", "#DDD");
    });

    // 指定部分でドロップしたときの処理
    $("#upload-area").on("drop", function(e){
        document.getElementById("upload-form-fileselect").files = e.originalEvent.dataTransfer.files;
        $("#upload-form-fileselect").change();
        $("#upload-area").css("background", "#DDD");
    });

    // ページ全体に乗っかっているときの処理
    $(document).on("dragover", function(e){
        e.preventDefault();
    });

    // ページ全体でドロップしたときの処理
    $(document).on("drop", function(e){
        e.preventDefault();
    });
});

完成サンプル

CSSはまったく整理してない

<HTML>

<div id="upload-area" style="height: 100px; width: 100px; background: #DDD; cursor: pointer;" onclick="$('#upload-form-fileselect').click();">
  うp<br>クリックもおk
</div>
<form enctype="multipart/form-data" action="/sandbox/upload.php" method="post" style="display: none;">
  <input type="file" id="upload-form-fileselect" name="files[]" multiple="multiple" onchange="$('#upload-form-submit').click();">
  <input type="submit" id="upload-form-submit" value="送信">
</form>

<JS>

$(document).ready(function(){
    $("#upload-area").on("dragenter", function(e){
        $("#upload-area").css("background", "#EEE");
    });
    $("#upload-area").on("dragleave", function(e){
        $("#upload-area").css("background", "#DDD");
    });
    $("#upload-area").on("drop", function(e){
        document.getElementById("upload-form-fileselect").files = e.originalEvent.dataTransfer.files;
        $("#upload-form-fileselect").change();
        $("#upload-area").css("background", "#DDD");
    });
    $(document).on("dragover", function(e){
        e.preventDefault();
    });
    $(document).on("drop", function(e){
        e.preventDefault();
    });
});