5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

input type="file" をまともにする

Posted at

ファイルアップロードのUI(input type="file")は厄介です。ブラウザによって表示方法が異なるのにCSSでのカスタマイズが難しいし、Chrome以外では添付したファイルをキャンセルできないという問題もあります。ですが、JavaScriptを使えば、

  1. CSSでカスタマイズ可能
  2. 添付したファイル名を表示可能
  3. 添付したファイルをキャンセル可能

にできます。

元のHTML

    <form method="POST" enctype="multipart/form-data">
        <ul>
            <li><input type="file" name="file1"></li>
            <li><input type="file" name="file2"></li>
        </ul>
        <input type="submit" value="UPLOAD">
    </form>

input type="file" な要素が2つあります。なのでJavaScriptでコントロールするときには対象を特定する必要があります。

修正版HTML

よくあるのは input type="file" な要素を非表示にした上で label 要素で囲み、label のクリックで input type="file" のポップアップを連動させるというやり方です。ですが、JavaScript を使うのであれば label にこだわる必要はありません。むしろ label にはデフォルトで連動のアクションがあるため扱いが面倒です。

label の代わりに span で囲むことにします。目印のために class="upload" としています。

    <form method="POST" enctype="multipart/form-data">
        <ul>
            <li><span class="upload">
                <input type="file" name="file1">
                <input disabled>
                </span></li>
            <li><span class="upload">
                <input type="file" name="file1">
                <input disabled>
                </span></li>
        </ul>
        <input type="submit" value="UPLOAD">
    </form>

span の中には2つ input 要素があります。一つは元々の input type="file" な要素、もう一つはファイル名を表示するために追加した要素です。追加した要素は表示のためだけに使うので、disabled にしています。アイコンなどを追加したい場合は span 要素の中に入れればクリック時にポップアップと連動します。

追加のCSS

以下のCSSを追加します。

    form .upload {
        display: inline-block;
    }
    form .upload input[type="file"] {
        display: none;
    }
    form .upload input[disabled] {
        pointer-events: none;
    }

span 要素はクリックを「受け止める」必要があるため、inline-block にします。元々の input 要素は非表示にし、追加した input 要素はクリックを「素通し」するので pointer-events: none; とします。この設定がないと Firefox では追加した input 要素がクリックを「消費」してしまい、span までクリックが伝わりません。

コントロール用JavaScript

以下のJavaScriptを追加します。

    $(function(){
        $('.upload').on('click', function(){
            $(this).find('input').val('');
            $(this).find('input[type="file"]').trigger('click');
        });
        $('.upload input[type="file"]').on('click', function(event){
            event.stopPropagation();
        });
        $('.upload input[type="file"]').on('change', function(){
            if (this.files.length) {
                $(this).parent().find('input[disabled]')
                    .val(this.files[0].name);
            }
        });
    });
  1. span 要素(class="upload")がクリックされたときは、内部にある2つの input 要素をともにクリアし、input type="file" な要素をクリックすることでポップアップを起動します。
  2. input type="file" な要素がクリックされたときに親要素へのイベント伝播を停止します。これを行わないと再度 span がクリックされることになるので無限ループになってしまいます。
  3. ポップアップから戻ったとき、ファイルが選択されていればそのファイル名を追加した方の input 要素に表示します。

jQueryを使ったので簡潔に書けました。生のDOM操作関数でも記述可能と思いますが、かなり面倒になると思います(私にはその根気はありません)。

5
7
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?