ファイルアップロードのUI(input type="file")は厄介です。ブラウザによって表示方法が異なるのにCSSでのカスタマイズが難しいし、Chrome以外では添付したファイルをキャンセルできないという問題もあります。ですが、JavaScriptを使えば、
- CSSでカスタマイズ可能
- 添付したファイル名を表示可能
- 添付したファイルをキャンセル可能
にできます。
元の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);
}
});
});
-
span
要素(class="upload"
)がクリックされたときは、内部にある2つのinput
要素をともにクリアし、input type="file"
な要素をクリックすることでポップアップを起動します。 -
input type="file"
な要素がクリックされたときに親要素へのイベント伝播を停止します。これを行わないと再度span
がクリックされることになるので無限ループになってしまいます。 - ポップアップから戻ったとき、ファイルが選択されていればそのファイル名を追加した方の
input
要素に表示します。
jQueryを使ったので簡潔に書けました。生のDOM操作関数でも記述可能と思いますが、かなり面倒になると思います(私にはその根気はありません)。
- 完成品 (見映えはCSSで修正)