基本的に、ファイルのアップロード等で使用するフォーム要素として
『input』要素の『type="file"』がありますが、
ファイル選択ボタンや、ファイル名表示エリアの位置やデザインは
基本的にブラウザ依存で、中々デザインを当て込むのは困難です。
デフォルトで適用されるデザインは、
IEやFirefoxはファイル選択ボタンがファイル名表示エリアに対して右側ですし、
ChromeやSafariはファイル選択ボタンがファイル名表示エリアに対して左側です。
ファイル未選択字のデフォルトメッセージ(『ファイルが選択されていません』等)も
言葉が異なっていたり、そもそも表示が無い等まちまちです。
これだけブラウザによって異なれば、
そのままデフォルトで使用すると
他のデザインとのバランスが悪くなったりして、
どうしてもデザインを入れたいパターンが出てくる場合があります。
また、ブラウザ依存のデザインなので、ブラウザによってデザインが異なるため、
UI的に迷いが生じる等を危惧して、全端末同じ様な見た目にする為に
デザインを適用する場合もあります。
今回は、そんなブラウザ依存のデザインが入ってしまっている
『input』要素の『type="file"』をカスタマイズして、好きなデザインに変えてしまう方法を
サンプルコード付きで解説します。
イベント起動などでは、分かりやすいのでjQueryを使用していますが、
勿論ネイティブのJavaScriptでも同じ事が出来ますので、
適当に置き換えて読み進めて下さい。
実際、jQueryを使用しているイベント駆動の部分でも、
そこまで複雑な事はしていないので、セレクタをドキュメントから書く必要がある程度の違いです。
要望があれば、ネイティブのJavaScriptへ書き直したものを追記しますので、
コメント頂ければ幸いです。
- まずは普通に『input』要素の『type="file"』を設置しよう。
- ファイル選択を起動させよう。
- ファイル選択状況表示エリアを実装しよう。
- 忘れがちなテキストボックス選択イベント
- Internet Explorer 対策
まずは普通に『input』要素の『type="file"』を設置しよう。
ファイル選択をさせてフォーム内に値を保持させるには
やはり『input』要素の『type="file"』が必要です。
まずは普通に『input』要素の『type="file"』を設置しましょう。
ファイル選択の機能は使いたいので、上記要素を設置する必要がありますが、
このままではデザインの乗っていないデフォルトのファイル選択フォームが表示されてしまいます。
今回は、デザインを自分で実装し、機能のみ上記要素を利用する手法をとりますので、
上記要素に対してスタイル『display:none』を適用して、見た目上、要素を消してしまいましょう。
続いて、要素を特定する為にファイル選択フォームに対してIDを振りましょう。
今回は、IDを【file_input】とします。
後に、非表示にした要素に対してイベントを送る事により、
ファイル選択を可能にします。
『display:none;』は、あくまで見た目上非表示にするスタイルですので、
機能はそのまま使用出来ますし、formでも問題なく送信出来ます。
ファイル選択を起動させよう。
ファイル選択フォームを立ち上げるには、先程作成した『input』要素の『type="file"』に対して
選択されたイベントを通知する事で、ブラウザへリクエストが走り、
ファイル選択ダイアログが出現します。
まずは、イベント起動のトリガーとなるボタンを設置しましょう。
今回は、標準の『input』要素の『type="button"』にて作成しますが、
勿論他の要素でも問題ありません。divにスタイルを当て込んだ角丸のボタンや
他にもイベント起動のアクションを取得出来るものであれば何でも可能です。
それでは、上記トリガー要素に対してイベント起動の設定を追加しましょう。
今回はボタンのクリックをトリガーにイベントを発生させたいので、
要素に直書きで『onclick』を追加しました。
勿論、イベント起動を外部に持たせる事も出来ますし、
クリックでなく、オンマウス起動でも問題ありません。
今回は分かりやすく、通常のファイル選択でもクリックイベントがトリガーですので、
『onclick』を使用します。
そして、先程お話致しました通り、ファイル選択もクリックイベントにて起動する事が出来ますので、
今回はjQueryにてクリックイベントを発生させました。
セレクタには先程作成したファイル選択フォームのID『file_input』を指定し、
クリックイベントを起動します。
上記の記述で、『onclick』にてクリックされたイベントをキャッチして
jQueryで記述されたファイル選択フォームに対してクリックイベントを発生させる処理を
起動します。
もう既に、『オリジナルファイル選択ボタン』を押せばファイル選択ダイアログだ立ち上がり、
ファイル選択でき、formにて送信する事が出来ます。
しかし、このまま現在の選択・未選択状態や、どのファイルが選択されているのかが分かりません。
利便性も欠きますし、デフォルトのファイル選択フォームでも
ファイル名表示エリアがありますので、次章でファイル情報の表示領域を実装しましょう。
ファイル選択状況表示エリアを実装しよう。
デフォルトのファイル選択フォームでは、選択状態や選択ファイルの表示を
テキストボックスの様なものに表示しています。
今回は、同じ様にテキストボックス内に選択されたファイルを表示させてみましょう。
まずはテキストボックスの設置です。
『input』要素の『type="text"』にてテキストボックスを作成します。
操作対象を特定する為に、上記要素にもIDとして『fake_input_file』を定義しています。
表示するファイル名は編集不可としたいので、『readonly="readonly"』にて
編集不可の読み取りのみ可能な状態を作り上げています。
後は、作成したテキストボックスに選択されたファイル名を表示させる処理を実装します。
ファイルの選択状態の変更イベントをトリガーにすると良いでしょう。
先程作成したファイル選択ボタン起動でのダイアログで
ファイル選択が完了すると、イベントが実行されたファイル選択フォーム『id="file_input"』に
ファイル名等が保持されますので、その値を利用します。
テキストボックスへ出力するタイミングは、『id="file_input"』の中に保持されたファイル名情報に
変更があった時としましょう。
外部ファイルでイベントを拾っても良いですが、
今回は分かりやすく『onchange』にてイベントを拾います。
早速、『onchange』を追記した要素を下記に示します。
ファイルの選択状態に変更があると『onchange』が起動されます。
『onchange』内では、セレクタとして先程作成したテキストボックスを指し、
その要素に対して値を設定する『val()』メソッドを起動しています。
セットする値を引数として渡しますが、その値は『$(this).val()』。
つまり、ファイル選択フォーム自体の値を引数として渡しています。
これでファイルの選択状態に変更が有るたびに、同じ情報をテキストボックスに表示させる事が可能です。
忘れがちなテキストボックス選択イベント
デフォルトのファイル選択フォームの挙動を出来るだけシミュレートしたいので、
ついつい忘れそうになるテキストボックス選択イベントも実装しましょう。
デフォルトでの挙動は、テキストボックス選択時にも、ファイル選択ダイアログが起動する動きです。
これは、ファイル選択ボタンと同じ挙動ですので、
ファイル選択ボタンに追記した『onclick』イベントをそのままコピーします。
これでファイル選択フォームをシミュレート出来る環境が整いました。
後は、ファイル選択ボタンやファイル選択情報表示テキストボックスの
位置やサイズ、デザイン等を好きにする事が出来ます。
例えば、下記の様なデザインも可能です。
サイトのデザインや利便性を考え、
最適な状態へ独自にカスタマイズしてくださいね。
中にはファイル選択ボタンは一纏めにして、内容をプレビューする為だけにテキストボックスが必要だとか、
アニメーションをつけて動きを派手にしたいといった事も、
コレまでのカスタマイズ方法を基本にすれば可能です。
纏めますと、
・機能を使う為にファイル選択フォームを非表示で設置。
・ファイル選択ダイアログを起動するには、非表示にしたファイル選択フォームへクリックイベントを送る。
・ファイル選択フォームの値に変更があれば、テキストボックス等へファイル選択情報を表示する。
・テキストボックスにもファイル選択ダイアログ起動イベントを実装しておく。
の4点です。
勿論、何をトリガーにダイアログを出しても良いですし、
ファイル情報をテキストボックス以外に表示しても問題ありません。
サイトの状況にあったカスタマイズを施して下さいね。
Internet Explorer 対策
これまで解説してきた内容では、IEで動かないことがあるようです。
どうやら、JavaScriptによって起動されたウィンドウでファイルを選択したとしても、
送信時に「アクセスが拒否されました」とログが出て、送信が停止してしまいます。
(何回か送信ボタンを押せば送信出来てしまうのですが、
これはこれでIEのバグ?なのかも知れません)
よくある対策としては、
<input type="file">を透明にして、目的のボタンの上に配置する方法ですが、
これは対象が複数ある時は不可能ですし、サイズや位置の特定が面倒です。
また、IEと他のモダンブラウザでは参照ボタンの位置が異なります。
更に、IEでは透明にしていたとしても、
ファイル名を直接打てるようなフォーカスが入ってしまいますので、美しくありません。
そこで、私が提案する方法は「<label>」要素を使う方法です。
「<label>」要素は、フォーム要素の部品と関連付けることが出来、
ラベルを選択した時にはフォーム部品と同じ動作をさせることが出来ます。
関連付けるフォーム要素を指定するには、フォーム要素のIDを「for」属性に対して指定します。
具体的には下記のような実装になるでしょう。
この様に、目的のフォーム要素のラベルとして参照ボタンを配置すれば、
ラベルをクリックする事でフォーム要素もクリックされた事になり、
問題なく参照ウィンドウが立ち上がり、送信も行えます。
私も透明に重ねたり、様々な方法を試しましたが、この方法が一番解りやすく
スマートだと考えます。
IEの動作で困っている方は、是非ともこの方法を試してみて下さい。